# quantmod  used to download stock data
#install.packages("quantmod")
library(quantmod)

# highcharter used to create interactive  charts
#install.packages("highcharter")
library(highcharter)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
# dygraphs used to create interactive candlestick charts
#install.packages("dygraphs")
library(dygraphs)

Week 1: Introduction

1.1 What Is Financial Analytics?

The art of transforming data into financial decisions using financial data analysis to answer questions like:

  • Which equity should I invest in?
  • Are banknotes genuine or forged?
  • Should we approve this client’s mortgage application?
  • What is a fair selling price for a property?
  • How do you balance assets to reduce risk?
  • Should we approve this
  • Should we approve this consumer’s credit card application?

1.2 Introduction to the Stock Market

1.3 Installing RStudio

1.4 Introduction to Coding with RStudio

1.5 More on Holding Period (Total) Return

Week 2: More on Investments

2.1 Wealth Index

2.2 Mean Periodic Return vs. Expected Return

Week 3: Market Indices, Treasury Bills

3.1 What Is a Stock Market Index?

# DJIA = SticlPrice1 + ... + StockPrice30 / Dow Divisor
dow_divisor <- 0.145

#Ex. KO, or any Blue Chip stock, increases by $1
DJIA <- round(1 / dow_divisor,2)
DJIA
[1] 6.9
# Market Cap = Stock Price * # of Outstanding Shares 
# Outstanding Shares = number of ALL shares issued by a company 
# S&P is based on ADJUSTED market cap of 500 largest US companies (505 becuase some companies have 2 stocks, like BRK-A and BRK-B)
# No. of Floating SHares are shares available for PUBLIC trading

# S&P Index = Stock Price 1 * No. of Floating Shares 1 + ... + Stock Price 505 * No. of Floating Shares 505 / Index Divisor

3.2 Stock Market Index Data with R

# Download data for DJIA (^DJI") and S&P (^GSPC)
getSymbols(c("^DJI","^GSPC"), from = "2000-1-1", to = "2020-4-1")
‘getSymbols’ currently uses auto.assign=TRUE by default, but will
use auto.assign=FALSE in 0.5-0. You will still be able to use
‘loadSymbols’ to automatically load data. getOption("getSymbols.env")
and getOption("getSymbols.auto.assign") will still be checked for
alternate defaults.

This message is shown once per session and may be disabled by setting 
options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.
[1] "^DJI"  "^GSPC"
# Inspect DJI
head(DJI)
           DJI.Open DJI.High  DJI.Low DJI.Close DJI.Volume DJI.Adjusted
2000-01-03 11501.85 11522.01 11305.69  11357.51    1697500     11357.51
2000-01-04 11349.75 11350.06 10986.45  10997.93    1784200     10997.93
2000-01-05 10989.37 11215.10 10938.67  11122.65    2031900     11122.65
2000-01-06 11113.37 11313.45 11098.45  11253.26    1765500     11253.26
2000-01-07 11247.06 11528.14 11239.92  11522.56    1849000     11522.56
2000-01-10 11532.48 11638.28 11532.48  11572.20    1681800     11572.20
# Inspect GSPC
head(GSPC)
           GSPC.Open GSPC.High GSPC.Low GSPC.Close GSPC.Volume GSPC.Adjusted
2000-01-03   1469.25   1478.00  1438.36    1455.22   931800000       1455.22
2000-01-04   1455.22   1455.22  1397.43    1399.42  1009000000       1399.42
2000-01-05   1399.42   1413.27  1377.68    1402.11  1085500000       1402.11
2000-01-06   1402.11   1411.90  1392.10    1403.45  1092300000       1403.45
2000-01-07   1403.45   1441.47  1400.73    1441.47  1225200000       1441.47
2000-01-10   1441.47   1464.36  1441.47    1457.60  1064800000       1457.60
# Create financial candlestick chart for DJI
chartSeries(DJI, type = "candlesticks", subset = "2020-1",theme="white")

# Create financial bar chart for DJI
# Tick to <- left is open value, tick to -> right is close value
chartSeries(DJI, type = "bars", subset = "2020-1::2020-3")

# Create interactive chart for DJI (open, high,low, and close)
dyRangeSelector(dyCandlestick(dygraph(DJI[ ,1:4])))
# Create candlestick chart with highcharter
hchart(DJI)

3.3 Importing Time Series Data into R

# "/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 3/va.xlsx"

va_file = "/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 3/va.csv"

# Download Virgin America data from an Excel/CSV File
VA <- read.csv(file = va_file)

head(VA)
tail(VA)

# Check the type of data. It is a DataFrame, not a time series object
str(VA)
'data.frame':   524 obs. of  6 variables:
 $ Day   : chr  "17-Nov-14" "18-Nov-14" "19-Nov-14" "20-Nov-14" ...
 $ Open  : num  30.3 33.7 38.5 32.8 35.8 ...
 $ High  : num  32.9 39.6 39.5 35 36.1 ...
 $ Low   : num  30 33.5 32.9 32.4 33.9 ...
 $ Volume: int  5640000 12830000 9080000 3120000 1870000 2230000 2060000 921480 2370000 2730000 ...
 $ Close : num  32.7 37 33.2 34 34.5 ...
# using the lookup table PDF, match the Day column with the encoding of the formatting to reformat the Day column
VA$Day <- as.Date(VA$Day, format = "%d-%b-%y")
tail(VA)
# Convert VA into xts object (accessible time series) from a DataFrame
# xts( Core data (non-time columns),  ), this is from importing quantmod
Branson <- xts(VA[ ,-1], order.by = VA$Day )

str(Branson)
An ‘xts’ object on 2014-11-17/2016-12-13 containing:
  Data: num [1:524, 1:5] 30.3 33.7 38.5 32.8 35.8 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:5] "Open" "High" "Low" "Volume" ...
  Indexed by objects of class: [Date] TZ: UTC
  xts Attributes:  
 NULL
tail(Branson)
            Open  High   Low  Volume Close
2016-12-06 56.80 56.95 56.70 7130000 56.80
2016-12-07 56.75 57.00 56.75 3020000 56.95
2016-12-08 57.00 57.05 56.95 2110000 57.00
2016-12-09 56.95 57.00 56.95  605760 57.00
2016-12-12 56.95 57.05 56.95 1320000 56.98
2016-12-13 56.95 57.05 56.95  417690 56.98
chartSeries(Branson, type = "candlesticks", subset = "2015-8")

3.4 Treasury Bills

# https://www.treasury.gov/resource-center/data-chart-center/interest-rates/Pages/TextView.aspx?data=yield
face_value <-  100
discount_rate <- 2.28 / 100
discount <- face_value * discount_rate
maturity <- 52

purchase_price  <- face_value - discount

# Total Return = (Ending Value - Beginning Value) / Beginning Value
total_return <- (face_value - purchase_price) / purchase_price
round(total_return * 100,2)
[1] 2.33
# Convert Annualized return to monthly return 
# 4 * 3-month return: 2.42 / 4 = 0.605%
# Ending Value = Beginning Value * (1 + Total Return)

# Approximate Solution
annualized_return <- 0.0242
months_in_a_year = 12

round(annualized_return / months_in_a_year, 6) * 100
[1] 0.2017
# Exact solution 
monthly_return <- 0.00605
round((1 + monthly_return) ^ (1/3) - 1,6)*100
[1] 0.2013
# Federal Reserve Bank of St. Louis. The symbol for FRED is DGS3MO  
# R Will download all available data (no to and from)
# This is the annualized return for 3 month treasury bills 
getSymbols("DGS3MO", src = "FRED")
[1] "DGS3MO"
head(DGS3MO)
           DGS3MO
1981-09-01  17.01
1981-09-02  16.65
1981-09-03  16.96
1981-09-04  16.64
1981-09-07     NA
1981-09-08  16.54
# Convert them into approximate monthly returns
# Divide them by 100 to get the proper percentage format 
risk.free <- DGS3MO / 100
risk.free <- DGS3MO / 12

# Divide by 12 to get approximate monthly return

# Or you can do (100 * 12 = 1200)
risk.free <- DGS3MO / 1200
head(risk.free)
               DGS3MO
1981-09-01 0.01417500
1981-09-02 0.01387500
1981-09-03 0.01413333
1981-09-04 0.01386667
1981-09-07         NA
1981-09-08 0.01378333

Week 4: Capital Asset Pricing Model

4.1 Objective

“RISK-FREE” TREASURY BILLS WITH GUARANTEED POSITIVE RETURN

RISKY APPLE STOCKS WITH HIGHER EXPECTED RETURN PREDICTED ; NOT GUARANTEED !

IS APPLE STOCKS’ EXPECTED RETURN HIGH ENOUGH TO JUSTIFY RISK ?

You will decide on if the expected return on Apple, Inc. stocks is high enough to justify risk. Use Capital Asset Pricing Model and predict the “fair” return on Apple, Inc. stocks. To do so, use the monthly return data from May 1, 2015 through April 30, 2020.

4.2 Beta [β]

Use R and download the index data for S&P500 and price data for Apple, Inc. stocks. Download the data between the first trading day of May 2015 and the last trading day of Apr 2020.

library(quantmod)
library(PerformanceAnalytics)

# S&P 500 Index and Apple Inc. 
getSymbols(c("^GSPC","AAPL"), from = "2015-5-1", to = "2020-5-1")
[1] "^GSPC" "AAPL" 
head(GSPC)
           GSPC.Open GSPC.High GSPC.Low GSPC.Close GSPC.Volume GSPC.Adjusted
2015-05-01   2087.38   2108.41  2087.38    2108.29  3379390000       2108.29
2015-05-04   2110.23   2120.95  2110.23    2114.49  3091580000       2114.49
2015-05-05   2112.63   2115.24  2088.46    2089.46  3793950000       2089.46
2015-05-06   2091.26   2098.42  2067.93    2080.15  3792210000       2080.15
2015-05-07   2079.96   2092.90  2074.99    2088.00  3676640000       2088.00
2015-05-08   2092.13   2117.66  2092.13    2116.10  3399440000       2116.10
head(AAPL)
           AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
2015-05-01   31.5250   32.5325  31.3250    32.2375   234050400      29.38875
2015-05-04   32.3750   32.6425  32.0650    32.1750   203953200      29.33177
2015-05-05   32.0375   32.1125  31.4450    31.4500   197085600      28.67084
2015-05-06   31.6400   31.6875  30.8400    31.2525   288564000      28.49079
2015-05-07   31.1925   31.5200  31.0050    31.3150   175763600      28.66702
2015-05-08   31.6700   31.9050  31.5275    31.9050   222201600      29.20712

Compute the monthly returns on AAPL and the S&P 500 Index.

apple.monthly <- monthlyReturn(AAPL$AAPL.Adjusted)

market.monthly <- monthlyReturn(GSPC$GSPC.Adjusted)

head(apple.monthly)
           monthly.returns
2015-05-29      0.01453417
2015-06-30     -0.03722746
2015-07-31     -0.03292672
2015-08-31     -0.06619624
2015-09-30     -0.02181624
2015-10-30      0.08340903
head(market.monthly)
           monthly.returns
2015-05-29   -0.0004269555
2015-06-30   -0.0210116724
2015-07-31    0.0197420297
2015-08-31   -0.0625808182
2015-09-30   -0.0264428316
2015-10-30    0.0829831178

Download the annualized return on 3-month Treasury Bills from the Federal Reserve Bank of Saint Louis. Then convert the annualized returns into approximate monthly returns.

getSymbols("DGS3MO", src="FRED")
[1] "DGS3MO"
head( DGS3MO )
           DGS3MO
1981-09-01  17.01
1981-09-02  16.65
1981-09-03  16.96
1981-09-04  16.64
1981-09-07     NA
1981-09-08  16.54

To convert the annualized returns into approximate monthly returns:  First, divide them by 100. e.g. 12.17% = 0.1217 and not 12.17

Then divide them by 12 to re-scale them to approximately one month

So divide each value by 100*12 = 1200

no.risk <- DGS3MO / 1200

head(no.risk)
               DGS3MO
1981-09-01 0.01417500
1981-09-02 0.01387500
1981-09-03 0.01413333
1981-09-04 0.01386667
1981-09-07         NA
1981-09-08 0.01378333

Compute the excess returns on AAPL and the S&P 500 Index in excess of 3-month Treasury Bills.

Both apple.monthly & no.risk are xts objects in R. They are self-aware of when they exist in time. Monthly excess return: “Reward” for taking risk

For xts objects, R computes apple.monthly − no.risk only if there is data for both objects on a given date.

EXCESS RETURN = PAST RETURN – RISK-FREE RETURN

apple.excess.monthly <- apple.monthly - no.risk

market.excess.monthly <- market.monthly - no.risk

head(apple.excess.monthly)
           monthly.returns
2015-05-29      0.01452583
2015-06-30     -0.03723579
2015-07-31     -0.03299339
2015-08-31     -0.06626291
2015-09-30     -0.02181624
2015-10-30      0.08334236
head(market.excess.monthly)
           monthly.returns
2015-05-29   -0.0004352888
2015-06-30   -0.0210200057
2015-07-31    0.0196753630
2015-08-31   -0.0626474848
2015-09-30   -0.0264428316
2015-10-30    0.0829164511

Predicting the beta for the Risky Asset

Plot Apple’s monthly excess returns versus the “U.S. market’s” monthly excess returns. How volatile is Apple stocks’ performance compared to the overall U.S. stock market?

Apple’s excess return -> Y (dependent variable) “Market” excess return -> X (explanatory variable)

chart.Regression(apple.excess.monthly, market.excess.monthly, fit = F)

# Create the Linear Model
cider <- lm(apple.excess.monthly ~ market.excess.monthly)

# Plot the Linear Model
chart.Regression(apple.excess.monthly, market.excess.monthly, fit = F)
abline(cider)

# View the equation

# y = Intercept + Coef * Value

# apple.excess.monthly = 0.011 + 1.165 * market.excess.monthly

# If market.excess.monthly increased by 1%, apple.excess.monthly increases by 1.165 * 1%

# 1.165 is the Predicted Beta

cider

Call:
lm(formula = apple.excess.monthly ~ market.excess.monthly)

Coefficients:
          (Intercept)  market.excess.monthly  
              0.01119                1.16453  

This means that we predict that Apple stock is 1.165 times more volatile than the overall market

4.3 Decision and Conclusion

4.4 Let’s Practice: JBLU

# Get the Data
getSymbols(c("JBLU","^GSPC"), from = "2015-5-1", to = "2020-5-1" )
[1] "JBLU"  "^GSPC"
getSymbols("DGS3MO", src="FRED")
[1] "DGS3MO"
# Shape Data
no.risk <- DGS3MO / 1200

# Calculate Monthly Returns
mint.monthly <- monthlyReturn(JBLU[ , 6])
market.monthly <- monthlyReturn(GSPC[ , 6])

# Calculate Excess 
mint.excess.monthly <- mint.monthly - no.risk
market.excess.monthly <- market.monthly - no.risk

# Create the Linear Model
plane <- lm(mint.excess.monthly ~ market.excess.monthly)

# Plot the Linear Model
chart.Regression(mint.excess.monthly, market.excess.monthly, fit = F)
abline(plane)


plane

Call:
lm(formula = mint.excess.monthly ~ market.excess.monthly)

Coefficients:
          (Intercept)  market.excess.monthly  
             -0.01562                1.36259  
# y = -0.01562 + 1.36259x

Based on the above predicted beta of approximately 1.36, it is predicted that JBLU stock is approximately 1.36 times more volatile compared the the overall market.

4.5 Issues with CAPM

Week 5: Building Portfolios with Risky Assets

5.1 Introduction

5.2 Risk-Adjusted Performance

Week 6: Optimizing Portfolios with Risky Assets

6.1 Preliminary Analysis

6.2 Introduction to Portfolio Optimization

6.3 Frontier of Portfolios

6.4 Extra Credit

Week 7: Machine Learning: K-Nearest Neighbors Algorithm

7.2 Introduction

7.3 A Brief Review

  • Suppose you are researching the weight distribution of dogs. You predict that the mean weight of male pugs is 16 lb and the standard deviation of weight is 3 lb.
  • Consider a male pug which weighs 22 lb.
  • What is the of the weight of this pug?

Z = (x - mean ) / sd

Z = (22 - 16) / 3 = 2

mean_lifespan <- 6.0
stdv <- 2.5
bluRay_Lifespan <- 3.0

Z <- (bluRay_Lifespan - mean_lifespan) / stdv


Z
[1] -1.2

Read in the data from the “FOOD.rdata” file

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 7/7_food.rdata")
head(FOOD)

Import the package

library(caret)
Loading required package: lattice
Loading required package: ggplot2

7.4 Introduction to KNN Algorithm

In this problem, CLASS is the dependent variable and GI and CALORIES are the independent variables

Train the model

FEAST <- train(CLASS ~ GI + CALORIES, data = FOOD, method = "knn")

FEAST
k-Nearest Neighbors 

14 samples
 2 predictor
 3 classes: 'fruit', 'nut', 'veggie' 

No pre-processing
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 14, 14, 14, 14, 14, 14, ... 
Resampling results across tuning parameters:

  k  Accuracy   Kappa    
  5  0.6124762  0.4674249
  7  0.5352381  0.3731238
  9  0.3572381  0.1616762

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 5.

A random sample of 14 raw food items. Let’s classify these raw food items: NUTS, VEGETABLES, FRUIT

Predict which class a coconut is

COCONUT <- data.frame(GI=53, CALORIES=369)
COCONUT
# predict ( model, dataframe )
predict(FEAST, COCONUT)
[1] nut
Levels: fruit nut veggie

KNN is a distance-based model. All predictors should be of similar same scale to be equally influential. Therefore, we must preprocess the data (transform the data). In this case, center and scale. Center subtracts the mean of the column from each value. Scale divides the result by the standard deviation of the result. In other words, the Z score. Regardless of the range of the original data, Z Scores typically range from ≈−3 to +3. All predictors have similar scales now.

FEAST <- train(CLASS ~ GI + CALORIES, data = FOOD, method = "knn", preProcess = c("center", "scale"))
There were missing values in resampled performance measures.
FEAST
k-Nearest Neighbors 

14 samples
 2 predictor
 3 classes: 'fruit', 'nut', 'veggie' 

Pre-processing: centered (2), scaled (2) 
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 14, 14, 14, 14, 14, 14, ... 
Resampling results across tuning parameters:

  k  Accuracy   Kappa    
  5  0.7071429  0.5643090
  7  0.5447619  0.3793763
  9  0.3963810  0.2438282

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 5.

This time, the predicted class of coconut is a Fruit

# predict ( model, dataframe )
predict(FEAST, COCONUT)
[1] fruit
Levels: fruit nut veggie

Predict the CLASS of jalapeno based on the KNN model FEAST.

JALAPENO <- data.frame(GI=30, CALORIES=29)
predict(FEAST, JALAPENO)
[1] veggie
Levels: fruit nut veggie

Predict the CLASS of macadamia based on the KNN model FEAST.

macadamia <- data.frame(GI=10, CALORIES=705)
predict(FEAST, macadamia)
[1] nut
Levels: fruit nut veggie

Predict the CLASS of mango based on the KNN model FEAST.

mango <- data.frame(GI=51, CALORIES=60)
predict(FEAST, mango)
[1] fruit
Levels: fruit nut veggie

7.5 A Financial Application of the KNN Algorithm

Load the data for Euro bank notes (2 data sets)

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 7/7_euro.rdata")

# Training data
head(EURO1)

# Testing Data
head(EURO2)
NA

Use the data in EURO1 and train a KNN model that predicts the CLASS of a Euro banknote

Train a model (set a tune length to tell it how many K values to try. By default, the function tries odd k values starting with k=5. Even k’s are not used to avoid ties.)

FORGER <- train(CLASS ~ . , data = EURO1, method = "knn", preProcess = c("center", "scale"), tuneLength = 20)

FORGER
k-Nearest Neighbors 

1000 samples
   4 predictor
   2 classes: 'forged', 'genuine' 

Pre-processing: centered (4), scaled (4) 
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 1000, 1000, 1000, 1000, 1000, 1000, ... 
Resampling results across tuning parameters:

  k   Accuracy   Kappa    
   5  0.9987025  0.9973795
   7  0.9972891  0.9945014
   9  0.9961957  0.9923088
  11  0.9960811  0.9920800
  13  0.9958809  0.9916858
  15  0.9953377  0.9905854
  17  0.9944424  0.9887717
  19  0.9937646  0.9874025
  21  0.9922616  0.9843830
  23  0.9910686  0.9819859
  25  0.9906800  0.9811901
  27  0.9907651  0.9813675
  29  0.9895615  0.9789385
  31  0.9894389  0.9787109
  33  0.9906523  0.9811515
  35  0.9908642  0.9815748
  37  0.9905401  0.9809231
  39  0.9908690  0.9815772
  41  0.9897775  0.9793803
  43  0.9901075  0.9800405

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 5.
GUESS <- predict(FORGER, EURO2)
tail(GUESS)
[1] genuine forged  genuine forged  genuine genuine
Levels: forged genuine

Build a confusion matrix for the results

confusionMatrix(GUESS, EURO2$CLASS)
Confusion Matrix and Statistics

          Reference
Prediction forged genuine
   forged     160       1
   genuine      0     211
                                          
               Accuracy : 0.9973          
                 95% CI : (0.9851, 0.9999)
    No Information Rate : 0.5699          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.9945          
                                          
 Mcnemar's Test P-Value : 1               
                                          
            Sensitivity : 1.0000          
            Specificity : 0.9953          
         Pos Pred Value : 0.9938          
         Neg Pred Value : 1.0000          
             Prevalence : 0.4301          
         Detection Rate : 0.4301          
   Detection Prevalence : 0.4328          
      Balanced Accuracy : 0.9976          
                                          
       'Positive' Class : forged          
                                          

Use the model and predict the CLASS of a banknote with the following characteristics: + VARIANCE=−2.97 + SKEWNESS=−10.33 + CURTOSIS=8.78 + ENTROPY=−2.11

MONEY <- data.frame(VARIANCE = -2.97, SKEWNESS = -10.33, CURTOSIS = 8.78, ENTROPY = -2.11)

predict(FORGER, MONEY)
[1] forged
Levels: forged genuine

Week 8: Machine Learning: Bayesian Binary Logistic Regression

8.1 More Review

Logarithm (log): log2^8= Which power of 2 gives us 8? 3

Euler’s number (e): an irrational number like pi. 2.7182818

loge^x = lnX = Natural log function of X

The Exponential function is the inverse of natural log

Probability Vs. Odds

Probability: + P(A) = n(A) / n(S) + What proportion of the time an outcome is to occur + The probability of rolling a 2 on a 6 sided die = 1/6 (0.167) + Maximum value = 100% + Minimum value = 0%

Odds: P / (1 - P) + Probability it will happen divided by the probability it will not happen
+ The odds that you will score a 2 = 0.167 / (1 - 0.167) = 0.2 [1/6 / (1 - 1/6) = 1/5] + Maximum value = Infinity + Minimum value = 0%

8.2 Introduction

# Load Library
library(caret)

# Load the data
load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 8/8_titanic.rdata")

head(Titanic)

Task: Build Bayesian Binary Logistic Regression that predicts the probability of surviving

ICEBERG <- train(SURVIVED ~ ., data = Titanic, method = "bayesglm")

summary(ICEBERG)

Call:
NULL

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.6177  -0.7001  -0.4382   0.6708   2.3833  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)  3.460249   0.321704  10.756  < 2e-16 ***
CLASSsecond -1.245829   0.222639  -5.596 2.20e-08 ***
CLASSthird  -2.249268   0.222658 -10.102  < 2e-16 ***
GENDERmale  -2.481615   0.164887 -15.050  < 2e-16 ***
AGE         -0.033539   0.006263  -5.355 8.55e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 1414.62  on 1045  degrees of freedom
Residual deviance:  982.66  on 1041  degrees of freedom
AIC: 992.66

Number of Fisher Scoring iterations: 6

Consider an 18 year old male passenger in 3rd class

JACK <- data.frame(CLASS='third', GENDER='male', AGE=18)

predict(ICEBERG, JACK, type = "prob")
NA

Consider an 18 year old female passenger in 1st class

ROSE <- data.frame(CLASS='first', GENDER='female', AGE=18)

predict(ICEBERG, ROSE, type = "prob")

8.3 Credit Risk Prediction

KNN: requires + Requires pre processing of predictor variables + Need to set.seed() for reproducible results + Can apply to problems with more that two classes Bayesian Binary Logistic Regression + Do not need to preprocess predictor variables + Do not need to set.seed() + Is not applicable to problems with more with more than two classes

# Load Library
library(caret)

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 8/8_home_loan.rdata")

head(HOME1)

head(HOME2)
# Set the seed for reproducible results. This will allow the bootstrapping of the KNN algorithm to use the same random numbers each time 
set.seed(13)
HGTV <- train(DEFAULT ~ ., data = HOME1, method = "knn", preProcess = c("center", "scale"), tuneLength = 20)

HGTV
k-Nearest Neighbors 

1000 samples
   5 predictor
   2 classes: 'no', 'yes' 

Pre-processing: centered (5), scaled (5) 
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 1000, 1000, 1000, 1000, 1000, 1000, ... 
Resampling results across tuning parameters:

  k   Accuracy   Kappa      
   5  0.5060711  0.009607512
   7  0.5071309  0.010885838
   9  0.5098630  0.015490393
  11  0.5174016  0.030451607
  13  0.5191932  0.033055891
  15  0.5242841  0.043797767
  17  0.5275205  0.050092521
  19  0.5305308  0.056826019
  21  0.5315578  0.058264064
  23  0.5330213  0.061659561
  25  0.5337244  0.062403006
  27  0.5394562  0.073723921
  29  0.5417160  0.078062340
  31  0.5445272  0.083097377
  33  0.5474947  0.089345388
  35  0.5467860  0.088104788
  37  0.5448651  0.084269635
  39  0.5458030  0.086530900
  41  0.5451493  0.085278917
  43  0.5465110  0.087969099

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 33.
# Define 
DIY <- train(DEFAULT ~.,data=HOME1,method="bayesglm")

# Define a potential customer
JOHNDOE <- data.frame(TYPE="cash", GENDER="male",CAR="yes",CHILDREN=2,RATIO=2.40)

# Will he default on his home loan?
predict(DIY,JOHNDOE, type = "prob")
# 17 0f the 33 (51.52%) nearest neighbors defaulted where as 16 (48.48%) did not
predict(HGTV,JOHNDOE, type = "prob")

Test the models

tail(HOME2)
# The KNN model is 55.75% accurate
# Accuracy : 0.56
hgtv <- predict(HGTV, HOME2)
# Build confusion matrix to check
confusionMatrix(hgtv, HOME2$DEFAULT)
Confusion Matrix and Statistics

          Reference
Prediction  no yes
       no   58  98
       yes  79 165
                                          
               Accuracy : 0.5575          
                 95% CI : (0.5073, 0.6068)
    No Information Rate : 0.6575          
    P-Value [Acc > NIR] : 1.0000          
                                          
                  Kappa : 0.0491          
                                          
 Mcnemar's Test P-Value : 0.1761          
                                          
            Sensitivity : 0.4234          
            Specificity : 0.6274          
         Pos Pred Value : 0.3718          
         Neg Pred Value : 0.6762          
             Prevalence : 0.3425          
         Detection Rate : 0.1450          
   Detection Prevalence : 0.3900          
      Balanced Accuracy : 0.5254          
                                          
       'Positive' Class : no              
                                          
# Logistic Regression is 60.25% accurate
# Accuracy : 0.6025 
diy <- predict(DIY, HOME2)
confusionMatrix(diy, HOME2$DEFAULT)
Confusion Matrix and Statistics

          Reference
Prediction  no yes
       no   35  57
       yes 102 206
                                          
               Accuracy : 0.6025          
                 95% CI : (0.5527, 0.6508)
    No Information Rate : 0.6575          
    P-Value [Acc > NIR] : 0.9905664       
                                          
                  Kappa : 0.0421          
                                          
 Mcnemar's Test P-Value : 0.0004841       
                                          
            Sensitivity : 0.2555          
            Specificity : 0.7833          
         Pos Pred Value : 0.3804          
         Neg Pred Value : 0.6688          
             Prevalence : 0.3425          
         Detection Rate : 0.0875          
   Detection Prevalence : 0.2300          
      Balanced Accuracy : 0.5194          
                                          
       'Positive' Class : no              
                                          

The “No Information Rate” is the most common class (in this case, 65.75% are default = yes)

Therefore, since both models did not perform better than the “No Information Rate”, neither model is a good one. In order to raise the accuracy, you will need to collect more data or add more predictor variables.

Week 9: Machine Learning: Introduction to Artificial Neural Networks

9.1 Introduction

Quantitative predictions such as “how much revenue will a movie generate” or “what would be a fair selling price for a property”

9.2 Artificial Neural Networks with Hyperbolic Tangent Activation Function

The Hyperbolic Tangent ranges from -1 to 1.

If X >= 3, then TanH is approximately equal to 1. If X <= -3, then TanH is approximately equal to -1

# Load the Data
load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 9/9_ames.rdata")

tail(ames)

Predict the fair selling price of a property.

*The data include: + Price: Selling price divided by $1,000,000 + Rooms: Number of rooms divided by 10 + Baths: Number of bathrooms divided by 10 + Kitchens: Number of kitchens divided by 10 + Cars: car capacity of garage divided by 10

When building artificial neural networks, the data should have “small” values (e.g. between -3 and 3) to satisfy the Hyperbolic Tangent. The “smaller”, the better.

#install.packages("neuralnet")
library(neuralnet)

LOGCABIN <- neuralnet(PRICE ~ ., data = ames, hidden = 1, act.fct = "tanh")

plot(LOGCABIN)

Each time you build the model, you will get different weights. The algorithm starts with random coefficients and converges to a nearby local optimum solution. The local optimum solution may differ because it uses different random numbers. You can set.seed() to reproduce results (the seed number does not matter) and tell the algorithm to use the same random numbers each time it is run.

set.seed(1)
LOGCABIN <- neuralnet(PRICE ~ ., data = ames, hidden = 1, act.fct = "tanh")

plot(LOGCABIN)

Anatomy: INPUT nodes (predictors), HIDDEN note(s), OUTPUT note (predicted). There are also “BIAS” terms (the blue lines). The numbers are the “weights”.

Step-by-step:

Use the model to predict the price of a house that has 7 rooms, 3 bathrooms, 1 kitchen, and a garage for 2 cars.

In the case of the above, the value of each predictor is multiplied by the weight. The BIAS terms are multiplied by the number in the node by it’s weight (in this case, 1 * 0.04719). See Below:

The sum of the above, 0.16015, is the value that flows into the HIDDEN NODE. The hidden node includes the activation function (in this case, the Hyperbolic Tangent).

1 * 0.04719
[1] 0.04719
0.7 * 0.06263
[1] 0.043841
0.3 * 0.16368
[1] 0.049104
0.1 * -0.28729
[1] -0.028729
0.2 * 0.24372
[1] 0.048744

The hidden node is going to apply the Hyperbolic Tangent function to the value of 0.16015.

FLOW <- (1 * 0.04719) + (0.7 * 0.06263) + (0.3 * 0.16368) + (0.1 * -0.28729)+ (0.2 * 0.24372)
FLOW
[1] 0.16015
tanh(FLOW)
[1] 0.1587947

The outflow from the HIDDEN NODE has it’s own weight, the the result of the Hyperbolic Tangent function is multiplied by the weight. Then the result is multiplied by the 2nd BIAS TERM, 1 * -0.05886. This is because both of those terms flow into the OUTPUT NODE. By default, there is no activation function in the OUTPUT NODE.

(0.1587947 * 1.69864) + (1 * -0.05886)
[1] 0.210875

Finally, convert the result to dollars by multiplying it by 1,000,000 (which is what we divided by in order to make the input values “small”)

OUTPUT <- (0.1587947 * 1.69864) + (1 * -0.05886)
HOME_PRICE <- OUTPUT  * 1000000
HOME_PRICE
[1] 210875

Using the Algorithm

Use the model to predict the price of a house that has 7 rooms, 3 bathrooms, 1 kitchen, and a garage for 2 cars.

NEST <- data.frame(ROOMS=7/10, BATHS=3/10, KITCHENS=1/10, CARS=2/10)

NEST
predict(LOGCABIN, NEST)
          [,1]
[1,] 0.2108809
predict(LOGCABIN, NEST) * 1000000
         [,1]
[1,] 210880.9

Use the model and predict the “fair” selling price of a house in Ames, Iowa, that has nine rooms, four bathrooms, one kitchen, and a garage for two cars.

“manual”

plot(LOGCABIN)

# Calculate Values: X * weight
BIAS1 <- 1 * 0.04719
rooms <- (9 / 10) * 0.06263
baths <- (4 / 10) * 0.16368
kitchens <- (1 / 10) * -0.28729
cars <- (2 / 10) * 0.24372
BIAS2 <- 1 * -0.05886

# Calculate the flow into the hidden node
HIDDEN = BIAS1 + rooms + baths + kitchens + cars

# Calculate the output
output <- (tanh(HIDDEN) * 1.69864) + BIAS2

# multiply the output to get predicted price
output * 1000000
[1] 258486.3

“automatic”

NEW_HOME <- data.frame(ROOMS=9/10, BATHS=4/10, KITCHENS=1/10, CARS=3/10)

predict(LOGCABIN, NEW_HOME) * 1000000
         [,1]
[1,] 298259.1

9.3 Artificial Neural Networks with Logistic (Sigmoid) Activation Function

# Logistic function 
plogis(2)
[1] 0.8807971

When the logistic functions is >= 6, it is asymptotic and virtually equal to 1. When the logistic functions is <= -6, it is asymptotic and virtually equal to 0.

library(neuralnet)

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 9/9_hollywood.rdata")

tail(HOLLYWOOD)

Suppose you are a movie producer and you want to predict how much revenue a movie will generate. For this purpose, you collect data from a random sample of 1,300 movies from the movie database (www.themoviedb.org). For each movie, the data set includes the following variables:

  • REVENUE : The revenue generated by the movie in $Billion
  • BUDGET : The budget required for producing the movie in $Billion.
  • RUNTIME : The runtime of the movie in hours.
  • HORROR : If the movie genre is horror or thriller, it equals 1. Otherwise, it is 0.
  • R.RATED : If the movie is R rated, it equals 1. Otherwise, it is 0.

Use R & build an artificial neural network that predicts the REVENUE generated from a movie. Include one hidden node in the artificial neural network. Use logistic (sigmoid) as the activation function.

# Set the seed
set.seed(1)

# Create the model with the logistic function
POPCORN <- neuralnet(REVENUE ~ ., data = HOLLYWOOD, hidden = 1, act.fct = "logistic")

# Plot the model
plot(POPCORN)

tail(HOLLYWOOD)
THE_GREY = data.frame(BUDGET=25000000/1000000000, RUNTIME=117/60, HORROR=0, R.RATED=1)

predict(POPCORN, THE_GREY) * 1000000000
         [,1]
[1,] 53262056
HALLOWEEN_ENDS <- data.frame(BUDGET=15000000/1000000000, RUNTIME=105/60, HORROR=1, R.RATED=1)

predict(POPCORN, HALLOWEEN_ENDS) * 1000000000
         [,1]
[1,] 38415556

10: Machine Learning: Training and Testing Artificial Neural Networks

10.1 More Review

Dependent Variable on the Y axis and independent variable on the X axis (Income depends on age)

2016 Annual income for celebrities on millions of dollars

library(scatterD3)

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 10/correlation.rdata")

head(CELEB)
plot(x = CELEB$AGE, y = CELEB$USD, xlab = "Age (2016)", ylab = "Income ($Million)")

There is a pattern, as AGE increases INCOME tends to go down.

scatterD3(x = CELEB$AGE, y = CELEB$USD, xlab = "Age (2016)", ylab = "Income ($Million)", color = "green", hover_size = 4)

Compute the correlation coefficient

# Age and USD
cor(CELEB$AGE, CELEB$US)
[1] -0.971771
# Age and USD
cor(CELEB$AGE, CELEB$EURO)
[1] -0.971771

The correlation coefficient is negative, which demonstrates a negative linear relationship between AGE And INCOME.(Pearson’s Correlation Coefficient -1 <= X <= 1)

10.2 Training and Testing Artificial Neural Network Models

Suppose you are a real estate agent at Nashville, TN. You want to predict the “fair” selling price of a property. For this purpose, you collect data from a random sample of 1,200 properties at Nashville, TN.

For each property, the data set includes the following information:

  • PRICE : The selling price of the property divided by $1,000,000
  • ACRES : The lot size in acres divided by 10
  • AREA : The living area in square-feet divided by 1,000
  • YEAR : The year when the property was built divided by 1,000
  • BATHS : The number of bathrooms divided by 10
  • BRICK : If the house has brick exterior wall, it equals 1; otherwise, 0.
  • BASEMENT : If the house has basement, it equals 1; otherwise, 0.

Use R & build an artificial neural network that predicts the PRICE of a property. Include four hidden nodes in the artificial neural network. Use logistic (sigmoid) as the activation function.

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 10/10_nashville.rdata")

head(NASH1)

head(NASH2)

Use R & build an artificial neural network that predicts the PRICE of a property. Include four hidden nodes in the artificial neural network. Use logistic (sigmoid) as the activation function.

library(neuralnet)

set.seed(99)
MANCAVE <- neuralnet(PRICE ~ ., data = NASH1, hidden = 4, act.fct = "logistic")

plot(MANCAVE)

Multiple linear regression model

SHESHED <- lm(PRICE ~ ., data = NASH1)
SHESHED

Call:
lm(formula = PRICE ~ ., data = NASH1)

Coefficients:
(Intercept)        ACRES         AREA         YEAR        BATHS        BRICK  
   1.931130    -0.027295     0.093312    -0.989820     0.303323     0.008696  
   BASEMENT  
   0.009422  
tail(NASH2)
mancave <- predict(MANCAVE, NASH2)
tail(mancave)
                       [,1]
811 N 6th St      0.1996185
1000 A Delmas Ave 0.1924331
2714 Rosedale Pl  0.2562977
237 Haverford Ave 0.1310566
352 Dade Dr       0.1986055
1118 Clay St      0.2479515

Test the Artificial Neural Network models performance with the correlation coefficient

cor(mancave, NASH2$PRICE)
          [,1]
[1,] 0.4287992
sheshed <- predict(SHESHED, NASH2)
tail(sheshed)
     811 N 6th St 1000 A Delmas Ave  2714 Rosedale Pl 237 Haverford Ave 
        0.2319040         0.2108066         0.2536210         0.1200185 
      352 Dade Dr      1118 Clay St 
        0.1832769         0.2332810 

Test the multiple linear regression models performance with the correlation coefficient

cor(sheshed, NASH2$PRICE)
[1] 0.3887337

Since the Artificial Neural Network model performed better, lets use it to make a prediction

# The following house sold for $165,000. Let's see how close the models get 

FORSALE <- data.frame(ACRES = 0.019, AREA = 1.566, YEAR = 1.984, BATHS = 0.3, BRICK = 1, BASEMENT = 0)

FORSALE
predict(SHESHED, FORSALE) * 1000000
       1 
212628.3 
predict(MANCAVE, FORSALE) * 1000000
         [,1]
[1,] 187509.7

10.3 Introduction to Deep Learning

Suppose you are a movie producer and you want to predict how much revenue a movie will generate. For this purpose, you collect data from a random sample of 1,767 movies from the movie database (www.themoviedb.org). For each movie, the data set includes the following variables:

  • REVENUE : The revenue generated by the movie in $Billion
  • BUDGET : The budget required for producing the movie in $Billion.
  • RUNTIME : The runtime of the movie in hours.
  • HORROR : If the movie genre is horror or thriller, it equals 1. Otherwise, it is 0.
  • R.RATED : If the movie is R rated, it equals 1. Otherwise, it is 0.

Use R & build an artificial neural network that predicts the REVENUE generated from a movie. Include three hidden nodes in the artificial neural network. Use logistic (sigmoid) as the activation function.

load("/Users/timhulak/Desktop/Syracuse/FIN-654\ Financial\ Analytics/Week\ 10/10_deep.rdata")

head(HOLLYWOOD)

head(WALKOFFAME)

Use R & build an artificial neural network that predicts the REVENUE generated from a movie. Include three hidden nodes in the artificial neural network. Use logistic (sigmoid) as the activation function.

library(neuralnet)

set.seed(5)
LOWBUDGET <- neuralnet(REVENUE ~ ., data = HOLLYWOOD, hidden = 3, act.fct = "logistic")

plot(LOWBUDGET)

Build a Deep ANN

Now build another model that has two hidden layers with three & two hidden nodes, respectively. Use logistic (sigmoid) as the activation function.

set.seed(3)
BLOCKBUSTER <- neuralnet(REVENUE ~ ., data = HOLLYWOOD, hidden = c(3,2), act.fct = "logistic")

plot(BLOCKBUSTER)

If the neural network has multiple hidden layers, it has deep learning architecture.

Test both models using the data frame WALKOFFAME. Use each model to predict the revenue from the movies in WALKOFFAME.

tail(WALKOFFAME)
lowbudget <- predict(LOWBUDGET, WALKOFFAME)

tail(lowbudget)
                             [,1]
The Mummy              0.30263066
Airport                0.04136593
A View to a Kill       0.09014952
Urban Legend           0.05160713
A Street Cat Named Bob 0.03007842
Saw                    0.03616818
cor(lowbudget, WALKOFFAME$REVENUE)
          [,1]
[1,] 0.7270871
blockbuster <- predict(BLOCKBUSTER, WALKOFFAME)

tail(blockbuster)
                             [,1]
The Mummy              0.33028681
Airport                0.02421945
A View to a Kill       0.08896215
Urban Legend           0.03639198
A Street Cat Named Bob 0.04235157
Saw                    0.02458765
cor(blockbuster, WALKOFFAME$REVENUE)
          [,1]
[1,] 0.7036656

The deep neural network model did not perform better than the single layer neural network (the simpler model displayed superior performance). This may be due to over-fitting the model. This means that you are going beyond the general pattern in the data and modeling the random noise in the dataset.

GRISWOLD <- data.frame(BUDGET = 0.027, RUNTIME = 1.6, HORROR = 0, R.RATED = 0)
GRISWOLD
predict(LOWBUDGET,GRISWOLD) * 1000000000
         [,1]
[1,] 72359835
predict(BLOCKBUSTER,GRISWOLD) * 1000000000
         [,1]
[1,] 68462710

The simpler model predicted 72,359,835 which is closer to 71,319,526

# This movie generated a revenue of $411.6 million
BATMAN <- data.frame(BUDGET = 0.035, RUNTIME = 2.1, HORROR = 0, R.RATED = 0)
BATMAN
predict(LOWBUDGET,BATMAN) * 1000000000
          [,1]
[1,] 101620712
predict(BLOCKBUSTER,BATMAN) * 1000000000
          [,1]
[1,] 106808829

Neither model was even close.

You can improve model performance by including more predictors, such as ACTION = 0 or 1.

LS0tCnRpdGxlOiAiRmluYW5jaWFsIEFuYWx5dGljcyIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogVGltIEh1bGFrCi0tLQoKYGBge3J9CiMgcXVhbnRtb2QgIHVzZWQgdG8gZG93bmxvYWQgc3RvY2sgZGF0YQojaW5zdGFsbC5wYWNrYWdlcygicXVhbnRtb2QiKQpsaWJyYXJ5KHF1YW50bW9kKQoKIyBoaWdoY2hhcnRlciB1c2VkIHRvIGNyZWF0ZSBpbnRlcmFjdGl2ZSAgY2hhcnRzCiNpbnN0YWxsLnBhY2thZ2VzKCJoaWdoY2hhcnRlciIpCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpCgojIGR5Z3JhcGhzIHVzZWQgdG8gY3JlYXRlIGludGVyYWN0aXZlIGNhbmRsZXN0aWNrIGNoYXJ0cwojaW5zdGFsbC5wYWNrYWdlcygiZHlncmFwaHMiKQpsaWJyYXJ5KGR5Z3JhcGhzKQpgYGAKCgojIFdlZWsgMTogSW50cm9kdWN0aW9uCgojIyMjIDEuMSBXaGF0IElzIEZpbmFuY2lhbCBBbmFseXRpY3M/CgpUaGUgYXJ0IG9mIHRyYW5zZm9ybWluZyBkYXRhIGludG8gZmluYW5jaWFsIGRlY2lzaW9ucyB1c2luZyBmaW5hbmNpYWwgZGF0YSBhbmFseXNpcyB0byBhbnN3ZXIgcXVlc3Rpb25zIGxpa2U6CgogICsgV2hpY2ggZXF1aXR5IHNob3VsZCBJIGludmVzdCBpbj8KICArIEFyZSBiYW5rbm90ZXMgZ2VudWluZSBvciBmb3JnZWQ/CiAgKyBTaG91bGQgd2UgYXBwcm92ZSB0aGlzIGNsaWVudCdzIG1vcnRnYWdlIGFwcGxpY2F0aW9uPwogICsgV2hhdCBpcyBhIGZhaXIgc2VsbGluZyBwcmljZSBmb3IgYSBwcm9wZXJ0eT8KICArIEhvdyBkbyB5b3UgYmFsYW5jZSBhc3NldHMgdG8gcmVkdWNlIHJpc2s/CiAgKyBTaG91bGQgd2UgYXBwcm92ZSB0aGlzIAogICsgU2hvdWxkIHdlIGFwcHJvdmUgdGhpcyBjb25zdW1lcidzIGNyZWRpdCBjYXJkIGFwcGxpY2F0aW9uPwoKIyMjIyAxLjIgSW50cm9kdWN0aW9uIHRvIHRoZSBTdG9jayBNYXJrZXQKYGBge3J9CgpgYGAKCiMjIyMgMS4zIEluc3RhbGxpbmcgUlN0dWRpbwpgYGB7cn0KCmBgYAoKIyMjIyAxLjQgSW50cm9kdWN0aW9uIHRvIENvZGluZyB3aXRoIFJTdHVkaW8KYGBge3J9CgpgYGAKCiMjIyMgMS41IE1vcmUgb24gSG9sZGluZyBQZXJpb2QgKFRvdGFsKSBSZXR1cm4KYGBge3J9CgpgYGAKCgojIFdlZWsgMjogTW9yZSBvbiBJbnZlc3RtZW50cwpgYGB7cn0KCmBgYAoKIyMjIyAyLjEgV2VhbHRoIEluZGV4CmBgYHtyfQoKYGBgCgojIyMjIDIuMiBNZWFuIFBlcmlvZGljIFJldHVybiB2cy4gRXhwZWN0ZWQgUmV0dXJuCmBgYHtyfQoKYGBgCgojIFdlZWsgMzogTWFya2V0IEluZGljZXMsIFRyZWFzdXJ5IEJpbGxzCgojIyMjIDMuMSBXaGF0IElzIGEgU3RvY2sgTWFya2V0IEluZGV4PwpgYGB7cn0KIyBESklBID0gU3RpY2xQcmljZTEgKyAuLi4gKyBTdG9ja1ByaWNlMzAgLyBEb3cgRGl2aXNvcgpkb3dfZGl2aXNvciA8LSAwLjE0NQoKI0V4LiBLTywgb3IgYW55IEJsdWUgQ2hpcCBzdG9jaywgaW5jcmVhc2VzIGJ5ICQxCkRKSUEgPC0gcm91bmQoMSAvIGRvd19kaXZpc29yLDIpCkRKSUEKYGBgCgpgYGB7cn0KIyBNYXJrZXQgQ2FwID0gU3RvY2sgUHJpY2UgKiAjIG9mIE91dHN0YW5kaW5nIFNoYXJlcyAKIyBPdXRzdGFuZGluZyBTaGFyZXMgPSBudW1iZXIgb2YgQUxMIHNoYXJlcyBpc3N1ZWQgYnkgYSBjb21wYW55IAojIFMmUCBpcyBiYXNlZCBvbiBBREpVU1RFRCBtYXJrZXQgY2FwIG9mIDUwMCBsYXJnZXN0IFVTIGNvbXBhbmllcyAoNTA1IGJlY3Vhc2Ugc29tZSBjb21wYW5pZXMgaGF2ZSAyIHN0b2NrcywgbGlrZSBCUkstQSBhbmQgQlJLLUIpCiMgTm8uIG9mIEZsb2F0aW5nIFNIYXJlcyBhcmUgc2hhcmVzIGF2YWlsYWJsZSBmb3IgUFVCTElDIHRyYWRpbmcKCiMgUyZQIEluZGV4ID0gU3RvY2sgUHJpY2UgMSAqIE5vLiBvZiBGbG9hdGluZyBTaGFyZXMgMSArIC4uLiArIFN0b2NrIFByaWNlIDUwNSAqIE5vLiBvZiBGbG9hdGluZyBTaGFyZXMgNTA1IC8gSW5kZXggRGl2aXNvcgoKCmBgYAoKCiMjIyMgMy4yIFN0b2NrIE1hcmtldCBJbmRleCBEYXRhIHdpdGggUgpgYGB7cn0KIyBEb3dubG9hZCBkYXRhIGZvciBESklBICheREpJIikgYW5kIFMmUCAoXkdTUEMpCmdldFN5bWJvbHMoYygiXkRKSSIsIl5HU1BDIiksIGZyb20gPSAiMjAwMC0xLTEiLCB0byA9ICIyMDIwLTQtMSIpCgojIEluc3BlY3QgREpJCmhlYWQoREpJKQoKYGBgCgpgYGB7cn0KIyBJbnNwZWN0IEdTUEMKaGVhZChHU1BDKQpgYGAKCmBgYHtyfQojIENyZWF0ZSBmaW5hbmNpYWwgY2FuZGxlc3RpY2sgY2hhcnQgZm9yIERKSQpjaGFydFNlcmllcyhESkksIHR5cGUgPSAiY2FuZGxlc3RpY2tzIiwgc3Vic2V0ID0gIjIwMjAtMSIsdGhlbWU9IndoaXRlIikKYGBgCgpgYGB7cn0KIyBDcmVhdGUgZmluYW5jaWFsIGJhciBjaGFydCBmb3IgREpJCiMgVGljayB0byA8LSBsZWZ0IGlzIG9wZW4gdmFsdWUsIHRpY2sgdG8gLT4gcmlnaHQgaXMgY2xvc2UgdmFsdWUKY2hhcnRTZXJpZXMoREpJLCB0eXBlID0gImJhcnMiLCBzdWJzZXQgPSAiMjAyMC0xOjoyMDIwLTMiKQpgYGAKCmBgYHtyfQojIENyZWF0ZSBpbnRlcmFjdGl2ZSBjaGFydCBmb3IgREpJIChvcGVuLCBoaWdoLGxvdywgYW5kIGNsb3NlKQpkeVJhbmdlU2VsZWN0b3IoZHlDYW5kbGVzdGljayhkeWdyYXBoKERKSVsgLDE6NF0pKSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgY2FuZGxlc3RpY2sgY2hhcnQgd2l0aCBoaWdoY2hhcnRlcgpoY2hhcnQoREpJKQpgYGAKCiMjIyMgMy4zIEltcG9ydGluZyBUaW1lIFNlcmllcyBEYXRhIGludG8gUgpgYGB7cn0KIyAiL1VzZXJzL3RpbWh1bGFrL0Rlc2t0b3AvU3lyYWN1c2UvRklOLTY1NFwgRmluYW5jaWFsXCBBbmFseXRpY3MvV2Vla1wgMy92YS54bHN4IgoKdmFfZmlsZSA9ICIvVXNlcnMvdGltaHVsYWsvRGVza3RvcC9TeXJhY3VzZS9GSU4tNjU0XCBGaW5hbmNpYWxcIEFuYWx5dGljcy9XZWVrXCAzL3ZhLmNzdiIKCiMgRG93bmxvYWQgVmlyZ2luIEFtZXJpY2EgZGF0YSBmcm9tIGFuIEV4Y2VsL0NTViBGaWxlClZBIDwtIHJlYWQuY3N2KGZpbGUgPSB2YV9maWxlKQoKaGVhZChWQSkKdGFpbChWQSkKCiMgQ2hlY2sgdGhlIHR5cGUgb2YgZGF0YS4gSXQgaXMgYSBEYXRhRnJhbWUsIG5vdCBhIHRpbWUgc2VyaWVzIG9iamVjdApzdHIoVkEpCmBgYAoKYGBge3J9CiMgdXNpbmcgdGhlIGxvb2t1cCB0YWJsZSBQREYsIG1hdGNoIHRoZSBEYXkgY29sdW1uIHdpdGggdGhlIGVuY29kaW5nIG9mIHRoZSBmb3JtYXR0aW5nIHRvIHJlZm9ybWF0IHRoZSBEYXkgY29sdW1uClZBJERheSA8LSBhcy5EYXRlKFZBJERheSwgZm9ybWF0ID0gIiVkLSViLSV5IikKdGFpbChWQSkKYGBgCmBgYHtyfQojIENvbnZlcnQgVkEgaW50byB4dHMgb2JqZWN0IChhY2Nlc3NpYmxlIHRpbWUgc2VyaWVzKSBmcm9tIGEgRGF0YUZyYW1lCiMgeHRzKCBDb3JlIGRhdGEgKG5vbi10aW1lIGNvbHVtbnMpLCAgKSwgdGhpcyBpcyBmcm9tIGltcG9ydGluZyBxdWFudG1vZApCcmFuc29uIDwtIHh0cyhWQVsgLC0xXSwgb3JkZXIuYnkgPSBWQSREYXkgKQoKc3RyKEJyYW5zb24pCgp0YWlsKEJyYW5zb24pCmBgYAoKYGBge3J9CmNoYXJ0U2VyaWVzKEJyYW5zb24sIHR5cGUgPSAiY2FuZGxlc3RpY2tzIiwgc3Vic2V0ID0gIjIwMTUtOCIpCmBgYAoKCiMjIyMgMy40IFRyZWFzdXJ5IEJpbGxzCmBgYHtyfQojIGh0dHBzOi8vd3d3LnRyZWFzdXJ5Lmdvdi9yZXNvdXJjZS1jZW50ZXIvZGF0YS1jaGFydC1jZW50ZXIvaW50ZXJlc3QtcmF0ZXMvUGFnZXMvVGV4dFZpZXcuYXNweD9kYXRhPXlpZWxkCmZhY2VfdmFsdWUgPC0gIDEwMApkaXNjb3VudF9yYXRlIDwtIDIuMjggLyAxMDAKZGlzY291bnQgPC0gZmFjZV92YWx1ZSAqIGRpc2NvdW50X3JhdGUKbWF0dXJpdHkgPC0gNTIKCnB1cmNoYXNlX3ByaWNlICA8LSBmYWNlX3ZhbHVlIC0gZGlzY291bnQKCiMgVG90YWwgUmV0dXJuID0gKEVuZGluZyBWYWx1ZSAtIEJlZ2lubmluZyBWYWx1ZSkgLyBCZWdpbm5pbmcgVmFsdWUKdG90YWxfcmV0dXJuIDwtIChmYWNlX3ZhbHVlIC0gcHVyY2hhc2VfcHJpY2UpIC8gcHVyY2hhc2VfcHJpY2UKcm91bmQodG90YWxfcmV0dXJuICogMTAwLDIpCgoKYGBgCgpgYGB7cn0KIyBDb252ZXJ0IEFubnVhbGl6ZWQgcmV0dXJuIHRvIG1vbnRobHkgcmV0dXJuIAojIDQgKiAzLW1vbnRoIHJldHVybjogMi40MiAvIDQgPSAwLjYwNSUKIyBFbmRpbmcgVmFsdWUgPSBCZWdpbm5pbmcgVmFsdWUgKiAoMSArIFRvdGFsIFJldHVybikKCiMgQXBwcm94aW1hdGUgU29sdXRpb24KYW5udWFsaXplZF9yZXR1cm4gPC0gMC4wMjQyCm1vbnRoc19pbl9hX3llYXIgPSAxMgoKcm91bmQoYW5udWFsaXplZF9yZXR1cm4gLyBtb250aHNfaW5fYV95ZWFyLCA2KSAqIDEwMAoKCgoKYGBgCgpgYGB7cn0KIyBFeGFjdCBzb2x1dGlvbiAKbW9udGhseV9yZXR1cm4gPC0gMC4wMDYwNQpyb3VuZCgoMSArIG1vbnRobHlfcmV0dXJuKSBeICgxLzMpIC0gMSw2KSoxMDAKYGBgCgpgYGB7cn0KIyBGZWRlcmFsIFJlc2VydmUgQmFuayBvZiBTdC4gTG91aXMuIFRoZSBzeW1ib2wgZm9yIEZSRUQgaXMgREdTM01PICAKIyBSIFdpbGwgZG93bmxvYWQgYWxsIGF2YWlsYWJsZSBkYXRhIChubyB0byBhbmQgZnJvbSkKIyBUaGlzIGlzIHRoZSBhbm51YWxpemVkIHJldHVybiBmb3IgMyBtb250aCB0cmVhc3VyeSBiaWxscyAKZ2V0U3ltYm9scygiREdTM01PIiwgc3JjID0gIkZSRUQiKQpoZWFkKERHUzNNTykKYGBgCgpgYGB7cn0KIyBDb252ZXJ0IHRoZW0gaW50byBhcHByb3hpbWF0ZSBtb250aGx5IHJldHVybnMKIyBEaXZpZGUgdGhlbSBieSAxMDAgdG8gZ2V0IHRoZSBwcm9wZXIgcGVyY2VudGFnZSBmb3JtYXQgCnJpc2suZnJlZSA8LSBER1MzTU8gLyAxMDAKcmlzay5mcmVlIDwtIERHUzNNTyAvIDEyCgojIERpdmlkZSBieSAxMiB0byBnZXQgYXBwcm94aW1hdGUgbW9udGhseSByZXR1cm4KCiMgT3IgeW91IGNhbiBkbyAoMTAwICogMTIgPSAxMjAwKQpyaXNrLmZyZWUgPC0gREdTM01PIC8gMTIwMApoZWFkKHJpc2suZnJlZSkKYGBgCgojIFdlZWsgNDogQ2FwaXRhbCBBc3NldCBQcmljaW5nIE1vZGVsCgojIyMjIDQuMSBPYmplY3RpdmUKCiJSSVNLLUZSRUUiIFRSRUFTVVJZIEJJTExTIFdJVEggR1VBUkFOVEVFRCBQT1NJVElWRSBSRVRVUk4gCgpSSVNLWSBBUFBMRSBTVE9DS1MgV0lUSCBISUdIRVIgRVhQRUNURUQgUkVUVVJOClBSRURJQ1RFRCA7IE5PVCBHVUFSQU5URUVEICEKCklTIEFQUExFIFNUT0NLUycgRVhQRUNURUQgUkVUVVJOIEhJR0ggRU5PVUdIIFRPIEpVU1RJRlkgUklTSyA/CgpZb3Ugd2lsbCBkZWNpZGUgb24gaWYgdGhlIGV4cGVjdGVkIHJldHVybiBvbiBBcHBsZSwgSW5jLiBzdG9ja3MgaXMgaGlnaCBlbm91Z2ggdG8ganVzdGlmeSByaXNrLgpVc2UgQ2FwaXRhbCBBc3NldCBQcmljaW5nIE1vZGVsIGFuZCBwcmVkaWN0IHRoZSDigJxmYWly4oCdIHJldHVybiBvbiBBcHBsZSwgSW5jLiBzdG9ja3MuClRvIGRvIHNvLCB1c2UgdGhlIG1vbnRobHkgcmV0dXJuIGRhdGEgZnJvbSBNYXkgMSwgMjAxNSB0aHJvdWdoIEFwcmlsIDMwLCAyMDIwLgoKIyMjIyA0LjIgQmV0YSBbzrJdCgoKVXNlIFIgYW5kIGRvd25sb2FkIHRoZSBpbmRleCBkYXRhIGZvciBTJlA1MDAgYW5kIHByaWNlIGRhdGEgZm9yIEFwcGxlLCBJbmMuIHN0b2Nrcy4KRG93bmxvYWQgdGhlIGRhdGEgYmV0d2VlbiB0aGUgZmlyc3QgdHJhZGluZyBkYXkgb2YgTWF5IDIwMTUgYW5kIHRoZSBsYXN0IHRyYWRpbmcgZGF5IG9mIEFwciAyMDIwLgoKYGBge3J9CmxpYnJhcnkocXVhbnRtb2QpCmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpCgojIFMmUCA1MDAgSW5kZXggYW5kIEFwcGxlIEluYy4gCmdldFN5bWJvbHMoYygiXkdTUEMiLCJBQVBMIiksIGZyb20gPSAiMjAxNS01LTEiLCB0byA9ICIyMDIwLTUtMSIpCgpoZWFkKEdTUEMpCmhlYWQoQUFQTCkKYGBgCgpDb21wdXRlIHRoZSBtb250aGx5IHJldHVybnMgb24gQUFQTCBhbmQgdGhlIFMmUCA1MDAgSW5kZXguCmBgYHtyfQphcHBsZS5tb250aGx5IDwtIG1vbnRobHlSZXR1cm4oQUFQTCRBQVBMLkFkanVzdGVkKQoKbWFya2V0Lm1vbnRobHkgPC0gbW9udGhseVJldHVybihHU1BDJEdTUEMuQWRqdXN0ZWQpCgpoZWFkKGFwcGxlLm1vbnRobHkpCmhlYWQobWFya2V0Lm1vbnRobHkpCmBgYAoKRG93bmxvYWQgdGhlIGFubnVhbGl6ZWQgcmV0dXJuIG9uIDMtbW9udGggVHJlYXN1cnkgQmlsbHMgZnJvbSB0aGUgRmVkZXJhbCBSZXNlcnZlIEJhbmsgb2YgU2FpbnQgTG91aXMuIFRoZW4gY29udmVydCB0aGUgYW5udWFsaXplZCByZXR1cm5zIGludG8gYXBwcm94aW1hdGUgbW9udGhseSByZXR1cm5zLgoKYGBge3J9CmdldFN5bWJvbHMoIkRHUzNNTyIsIHNyYz0iRlJFRCIpCgpoZWFkKCBER1MzTU8gKQpgYGAKClRvIGNvbnZlcnQgdGhlIGFubnVhbGl6ZWQgcmV0dXJucyBpbnRvIGFwcHJveGltYXRlIG1vbnRobHkgcmV0dXJuczog74KRIEZpcnN0LCBkaXZpZGUgdGhlbSBieSAxMDAuIGUuZy4gMTIuMTclID0gMC4xMjE3IGFuZCBub3QgMTIuMTcKClRoZW4gZGl2aWRlIHRoZW0gYnkgMTIgdG8gcmUtc2NhbGUgdGhlbSB0byBhcHByb3hpbWF0ZWx5IG9uZSBtb250aAoKU28gZGl2aWRlIGVhY2ggdmFsdWUgYnkgMTAwKjEyID0gMTIwMAoKYGBge3J9Cm5vLnJpc2sgPC0gREdTM01PIC8gMTIwMAoKaGVhZChuby5yaXNrKQpgYGAKCkNvbXB1dGUgdGhlIGV4Y2VzcyByZXR1cm5zIG9uIEFBUEwgYW5kIHRoZSBTJlAgNTAwIEluZGV4IGluIGV4Y2VzcyBvZiAzLW1vbnRoIFRyZWFzdXJ5IEJpbGxzLgoKQm90aCBhcHBsZS5tb250aGx5ICYgbm8ucmlzayBhcmUgeHRzIG9iamVjdHMgaW4gUi4gVGhleSBhcmUgc2VsZi1hd2FyZSBvZiB3aGVuIHRoZXkgZXhpc3QgaW4gdGltZS4KTW9udGhseSBleGNlc3MgcmV0dXJuOiAiUmV3YXJkIiBmb3IgdGFraW5nIHJpc2sKCkZvciB4dHMgb2JqZWN0cywgUiBjb21wdXRlcyBhcHBsZS5tb250aGx5IOKIkiBuby5yaXNrIG9ubHkgaWYgdGhlcmUgaXMgZGF0YSBmb3IgYm90aCBvYmplY3RzIG9uIGEgZ2l2ZW4gZGF0ZS4KCkVYQ0VTUyBSRVRVUk4gPSBQQVNUIFJFVFVSTiDigJMgUklTSy1GUkVFIFJFVFVSTgoKYGBge3J9CmFwcGxlLmV4Y2Vzcy5tb250aGx5IDwtIGFwcGxlLm1vbnRobHkgLSBuby5yaXNrCgptYXJrZXQuZXhjZXNzLm1vbnRobHkgPC0gbWFya2V0Lm1vbnRobHkgLSBuby5yaXNrCgpoZWFkKGFwcGxlLmV4Y2Vzcy5tb250aGx5KQoKaGVhZChtYXJrZXQuZXhjZXNzLm1vbnRobHkpCmBgYAoKUHJlZGljdGluZyB0aGUgYmV0YSBmb3IgdGhlIFJpc2t5IEFzc2V0CgpQbG90IEFwcGxlJ3MgbW9udGhseSBleGNlc3MgcmV0dXJucyB2ZXJzdXMgdGhlICJVLlMuIG1hcmtldCdzIiBtb250aGx5IGV4Y2VzcyByZXR1cm5zLiBIb3cgdm9sYXRpbGUgaXMgQXBwbGUgc3RvY2tzJyBwZXJmb3JtYW5jZSBjb21wYXJlZCB0byB0aGUgb3ZlcmFsbCBVLlMuIHN0b2NrIG1hcmtldD8KCkFwcGxl4oCZcyBleGNlc3MgcmV0dXJuIC0+IFkgKGRlcGVuZGVudCB2YXJpYWJsZSkK4oCcTWFya2V04oCdIGV4Y2VzcyByZXR1cm4gLT4gWCAoZXhwbGFuYXRvcnkgdmFyaWFibGUpCgpgYGB7cn0KY2hhcnQuUmVncmVzc2lvbihhcHBsZS5leGNlc3MubW9udGhseSwgbWFya2V0LmV4Y2Vzcy5tb250aGx5LCBmaXQgPSBGKQpgYGAKCmBgYHtyfQojIENyZWF0ZSB0aGUgTGluZWFyIE1vZGVsCmNpZGVyIDwtIGxtKGFwcGxlLmV4Y2Vzcy5tb250aGx5IH4gbWFya2V0LmV4Y2Vzcy5tb250aGx5KQoKIyBQbG90IHRoZSBMaW5lYXIgTW9kZWwKY2hhcnQuUmVncmVzc2lvbihhcHBsZS5leGNlc3MubW9udGhseSwgbWFya2V0LmV4Y2Vzcy5tb250aGx5LCBmaXQgPSBGKQphYmxpbmUoY2lkZXIpCmBgYApgYGB7cn0KIyBWaWV3IHRoZSBlcXVhdGlvbgoKIyB5ID0gSW50ZXJjZXB0ICsgQ29lZiAqIFZhbHVlCgojIGFwcGxlLmV4Y2Vzcy5tb250aGx5ID0gMC4wMTEgKyAxLjE2NSAqIG1hcmtldC5leGNlc3MubW9udGhseQoKIyBJZiBtYXJrZXQuZXhjZXNzLm1vbnRobHkgaW5jcmVhc2VkIGJ5IDElLCBhcHBsZS5leGNlc3MubW9udGhseSBpbmNyZWFzZXMgYnkgMS4xNjUgKiAxJQoKIyAxLjE2NSBpcyB0aGUgUHJlZGljdGVkIEJldGEKCmNpZGVyCmBgYAoKVGhpcyBtZWFucyB0aGF0IHdlIHByZWRpY3QgdGhhdCBBcHBsZSBzdG9jayBpcyAxLjE2NSB0aW1lcyBtb3JlIHZvbGF0aWxlIHRoYW4gdGhlIG92ZXJhbGwgbWFya2V0IAoKCgojIyMjIDQuMyBEZWNpc2lvbiBhbmQgQ29uY2x1c2lvbgpgYGB7cn0KCmBgYAoKCiMjIyMgNC40IExldCdzIFByYWN0aWNlOiBKQkxVCmBgYHtyfQojIEdldCB0aGUgRGF0YQpnZXRTeW1ib2xzKGMoIkpCTFUiLCJeR1NQQyIpLCBmcm9tID0gIjIwMTUtNS0xIiwgdG8gPSAiMjAyMC01LTEiICkKZ2V0U3ltYm9scygiREdTM01PIiwgc3JjPSJGUkVEIikKCiMgU2hhcGUgRGF0YQpuby5yaXNrIDwtIERHUzNNTyAvIDEyMDAKCiMgQ2FsY3VsYXRlIE1vbnRobHkgUmV0dXJucwptaW50Lm1vbnRobHkgPC0gbW9udGhseVJldHVybihKQkxVWyAsIDZdKQptYXJrZXQubW9udGhseSA8LSBtb250aGx5UmV0dXJuKEdTUENbICwgNl0pCgojIENhbGN1bGF0ZSBFeGNlc3MgCm1pbnQuZXhjZXNzLm1vbnRobHkgPC0gbWludC5tb250aGx5IC0gbm8ucmlzawptYXJrZXQuZXhjZXNzLm1vbnRobHkgPC0gbWFya2V0Lm1vbnRobHkgLSBuby5yaXNrCgojIENyZWF0ZSB0aGUgTGluZWFyIE1vZGVsCnBsYW5lIDwtIGxtKG1pbnQuZXhjZXNzLm1vbnRobHkgfiBtYXJrZXQuZXhjZXNzLm1vbnRobHkpCgojIFBsb3QgdGhlIExpbmVhciBNb2RlbApjaGFydC5SZWdyZXNzaW9uKG1pbnQuZXhjZXNzLm1vbnRobHksIG1hcmtldC5leGNlc3MubW9udGhseSwgZml0ID0gRikKYWJsaW5lKHBsYW5lKQoKcGxhbmUKIyB5ID0gLTAuMDE1NjIgKyAxLjM2MjU5eAoKYGBgCgpCYXNlZCBvbiB0aGUgYWJvdmUgcHJlZGljdGVkIGJldGEgb2YgYXBwcm94aW1hdGVseSAxLjM2LCBpdCBpcyBwcmVkaWN0ZWQgdGhhdCBKQkxVIHN0b2NrIGlzIGFwcHJveGltYXRlbHkgMS4zNiB0aW1lcyBtb3JlIHZvbGF0aWxlIGNvbXBhcmVkIHRoZSB0aGUgb3ZlcmFsbCBtYXJrZXQuIAoKIyMjIyA0LjUgSXNzdWVzIHdpdGggQ0FQTQpgYGB7cn0KCmBgYAoKIyBXZWVrIDU6IEJ1aWxkaW5nIFBvcnRmb2xpb3Mgd2l0aCBSaXNreSBBc3NldHMKYGBge3J9CgpgYGAKCiMjIyMgNS4xIEludHJvZHVjdGlvbgpgYGB7cn0KCmBgYAoKIyMjIyA1LjIgUmlzay1BZGp1c3RlZCBQZXJmb3JtYW5jZQpgYGB7cn0KCmBgYAoKIyBXZWVrIDY6IE9wdGltaXppbmcgUG9ydGZvbGlvcyB3aXRoIFJpc2t5IEFzc2V0cwpgYGB7cn0KCmBgYAoKIyMjIyA2LjEgUHJlbGltaW5hcnkgQW5hbHlzaXMKYGBge3J9CgpgYGAKCiMjIyMgNi4yIEludHJvZHVjdGlvbiB0byBQb3J0Zm9saW8gT3B0aW1pemF0aW9uCmBgYHtyfQoKYGBgCgojIyMjIDYuMyBGcm9udGllciBvZiBQb3J0Zm9saW9zCmBgYHtyfQoKYGBgCgojIyMjIDYuNCBFeHRyYSBDcmVkaXQKYGBge3J9CgpgYGAKCiMgV2VlayA3OiBNYWNoaW5lIExlYXJuaW5nOiBLLU5lYXJlc3QgTmVpZ2hib3JzIEFsZ29yaXRobQpgYGB7cn0KCmBgYAoKCiMjIyMgNy4yIEludHJvZHVjdGlvbgpgYGB7cn0KCmBgYAoKIyMjIyA3LjMgQSBCcmllZiBSZXZpZXcKCiAgKyBTdXBwb3NlIHlvdSBhcmUgcmVzZWFyY2hpbmcgdGhlIHdlaWdodCBkaXN0cmlidXRpb24gb2YgZG9ncy4gWW91IHByZWRpY3QgdGhhdCB0aGUgbWVhbiB3ZWlnaHQgb2YgbWFsZSBwdWdzIGlzIDE2IGxiIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHdlaWdodCBpcyAzIGxiLgogICsgQ29uc2lkZXIgYSBtYWxlIHB1ZyB3aGljaCB3ZWlnaHMgMjIgbGIuCiAgKyBXaGF0IGlzIHRoZSBvZiB0aGUgd2VpZ2h0IG9mIHRoaXMgcHVnPwogIApaID0gKHggLSBtZWFuICkgLyBzZAoKWiA9ICgyMiAtIDE2KSAvIDMgPSAyCgpgYGB7cn0KbWVhbl9saWZlc3BhbiA8LSA2LjAKc3RkdiA8LSAyLjUKYmx1UmF5X0xpZmVzcGFuIDwtIDMuMAoKWiA8LSAoYmx1UmF5X0xpZmVzcGFuIC0gbWVhbl9saWZlc3BhbikgLyBzdGR2CgoKWgpgYGAKCgpSZWFkIGluIHRoZSBkYXRhIGZyb20gdGhlICJGT09ELnJkYXRhIiBmaWxlIApgYGB7cn0KbG9hZCgiL1VzZXJzL3RpbWh1bGFrL0Rlc2t0b3AvU3lyYWN1c2UvRklOLTY1NFwgRmluYW5jaWFsXCBBbmFseXRpY3MvV2Vla1wgNy83X2Zvb2QucmRhdGEiKQpoZWFkKEZPT0QpCmBgYAoKIyBJbXBvcnQgdGhlIHBhY2thZ2UgCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpgYGAKCiMjIyMgNy40IEludHJvZHVjdGlvbiB0byBLTk4gQWxnb3JpdGhtCgpJbiB0aGlzIHByb2JsZW0sIENMQVNTIGlzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIEdJIGFuZCBDQUxPUklFUyBhcmUgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyAKClRyYWluIHRoZSBtb2RlbAoKYGBge3J9CkZFQVNUIDwtIHRyYWluKENMQVNTIH4gR0kgKyBDQUxPUklFUywgZGF0YSA9IEZPT0QsIG1ldGhvZCA9ICJrbm4iKQoKRkVBU1QKYGBgCgpBIHJhbmRvbSBzYW1wbGUgb2YgMTQgcmF3IGZvb2QgaXRlbXMuIExldCdzIGNsYXNzaWZ5IHRoZXNlIHJhdyBmb29kIGl0ZW1zOiBOVVRTLCBWRUdFVEFCTEVTLCBGUlVJVAoKClByZWRpY3Qgd2hpY2ggY2xhc3MgYSBjb2NvbnV0IGlzCmBgYHtyfQpDT0NPTlVUIDwtIGRhdGEuZnJhbWUoR0k9NTMsIENBTE9SSUVTPTM2OSkKQ09DT05VVApgYGAKCmBgYHtyfQojIHByZWRpY3QgKCBtb2RlbCwgZGF0YWZyYW1lICkKcHJlZGljdChGRUFTVCwgQ09DT05VVCkKYGBgCgpLTk4gaXMgYSBkaXN0YW5jZS1iYXNlZCBtb2RlbC4gQWxsIHByZWRpY3RvcnMgc2hvdWxkIGJlIG9mIHNpbWlsYXIgc2FtZSBzY2FsZSB0byBiZSBlcXVhbGx5IGluZmx1ZW50aWFsLiBUaGVyZWZvcmUsIHdlIG11c3QgcHJlcHJvY2VzcyB0aGUgZGF0YSAodHJhbnNmb3JtIHRoZSBkYXRhKS4gSW4gdGhpcyBjYXNlLCBjZW50ZXIgYW5kIHNjYWxlLiBDZW50ZXIgc3VidHJhY3RzIHRoZSBtZWFuIG9mIHRoZSBjb2x1bW4gZnJvbSBlYWNoIHZhbHVlLiBTY2FsZSBkaXZpZGVzIHRoZSByZXN1bHQgYnkgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcmVzdWx0LiBJbiBvdGhlciB3b3JkcywgdGhlIFogc2NvcmUuIFJlZ2FyZGxlc3Mgb2YgdGhlIHJhbmdlIG9mIHRoZSBvcmlnaW5hbCBkYXRhLCBaIFNjb3JlcyB0eXBpY2FsbHkgcmFuZ2UgZnJvbSDiiYjiiJIzIHRvICszLiBBbGwgcHJlZGljdG9ycyBoYXZlIHNpbWlsYXIgc2NhbGVzIG5vdy4KCmBgYHtyfQpGRUFTVCA8LSB0cmFpbihDTEFTUyB+IEdJICsgQ0FMT1JJRVMsIGRhdGEgPSBGT09ELCBtZXRob2QgPSAia25uIiwgcHJlUHJvY2VzcyA9IGMoImNlbnRlciIsICJzY2FsZSIpKQoKRkVBU1QKYGBgCgpUaGlzIHRpbWUsIHRoZSBwcmVkaWN0ZWQgY2xhc3Mgb2YgY29jb251dCBpcyBhIEZydWl0CmBgYHtyfQojIHByZWRpY3QgKCBtb2RlbCwgZGF0YWZyYW1lICkKcHJlZGljdChGRUFTVCwgQ09DT05VVCkKYGBgClByZWRpY3QgdGhlIENMQVNTIG9mIGphbGFwZW5vIGJhc2VkIG9uIHRoZSBLTk4gbW9kZWwgRkVBU1QuCmBgYHtyfQpKQUxBUEVOTyA8LSBkYXRhLmZyYW1lKEdJPTMwLCBDQUxPUklFUz0yOSkKcHJlZGljdChGRUFTVCwgSkFMQVBFTk8pCmBgYAoKUHJlZGljdCB0aGUgQ0xBU1Mgb2YgbWFjYWRhbWlhIGJhc2VkIG9uIHRoZSBLTk4gbW9kZWwgRkVBU1QuCmBgYHtyfQptYWNhZGFtaWEgPC0gZGF0YS5mcmFtZShHST0xMCwgQ0FMT1JJRVM9NzA1KQpwcmVkaWN0KEZFQVNULCBtYWNhZGFtaWEpCmBgYAoKUHJlZGljdCB0aGUgQ0xBU1Mgb2YgbWFuZ28gYmFzZWQgb24gdGhlIEtOTiBtb2RlbCBGRUFTVC4KYGBge3J9Cm1hbmdvIDwtIGRhdGEuZnJhbWUoR0k9NTEsIENBTE9SSUVTPTYwKQpwcmVkaWN0KEZFQVNULCBtYW5nbykKYGBgCgoKIyMjIyA3LjUgQSBGaW5hbmNpYWwgQXBwbGljYXRpb24gb2YgdGhlIEtOTiBBbGdvcml0aG0KCkxvYWQgdGhlIGRhdGEgZm9yIEV1cm8gYmFuayBub3RlcyAoMiBkYXRhIHNldHMpCmBgYHtyfQpsb2FkKCIvVXNlcnMvdGltaHVsYWsvRGVza3RvcC9TeXJhY3VzZS9GSU4tNjU0XCBGaW5hbmNpYWxcIEFuYWx5dGljcy9XZWVrXCA3LzdfZXVyby5yZGF0YSIpCgojIFRyYWluaW5nIGRhdGEKaGVhZChFVVJPMSkKCiMgVGVzdGluZyBEYXRhCmhlYWQoRVVSTzIpCgpgYGAKCgpVc2UgdGhlIGRhdGEgaW4gRVVSTzEgYW5kIHRyYWluIGEgS05OIG1vZGVsIHRoYXQgcHJlZGljdHMgdGhlIENMQVNTIG9mIGEgRXVybyBiYW5rbm90ZQoKVHJhaW4gYSBtb2RlbCAoc2V0IGEgdHVuZSBsZW5ndGggdG8gdGVsbCBpdCBob3cgbWFueSBLIHZhbHVlcyB0byB0cnkuIEJ5IGRlZmF1bHQsIHRoZSBmdW5jdGlvbiB0cmllcyBvZGQgayB2YWx1ZXMgc3RhcnRpbmcgd2l0aCBrPTUuIEV2ZW4gaydzIGFyZSBub3QgdXNlZCB0byBhdm9pZCB0aWVzLikKYGBge3J9CkZPUkdFUiA8LSB0cmFpbihDTEFTUyB+IC4gLCBkYXRhID0gRVVSTzEsIG1ldGhvZCA9ICJrbm4iLCBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksIHR1bmVMZW5ndGggPSAyMCkKCkZPUkdFUgpgYGAKCmBgYHtyfQpHVUVTUyA8LSBwcmVkaWN0KEZPUkdFUiwgRVVSTzIpCnRhaWwoR1VFU1MpCmBgYAoKQnVpbGQgYSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgcmVzdWx0cwpgYGB7cn0KY29uZnVzaW9uTWF0cml4KEdVRVNTLCBFVVJPMiRDTEFTUykKYGBgCgpVc2UgdGhlIG1vZGVsIGFuZCBwcmVkaWN0IHRoZSBDTEFTUyBvZiBhIGJhbmtub3RlIHdpdGggdGhlIGZvbGxvd2luZyBjaGFyYWN0ZXJpc3RpY3M6IAogICsgVkFSSUFOQ0U94oiSMi45NyAKICArIFNLRVdORVNTPeKIkjEwLjMzIAogICsgQ1VSVE9TSVM9OC43OCAKICArIEVOVFJPUFk94oiSMi4xMQogIApgYGB7cn0KTU9ORVkgPC0gZGF0YS5mcmFtZShWQVJJQU5DRSA9IC0yLjk3LCBTS0VXTkVTUyA9IC0xMC4zMywgQ1VSVE9TSVMgPSA4Ljc4LCBFTlRST1BZID0gLTIuMTEpCgpwcmVkaWN0KEZPUkdFUiwgTU9ORVkpCmBgYAoKIyBXZWVrIDg6IE1hY2hpbmUgTGVhcm5pbmc6IEJheWVzaWFuIEJpbmFyeSBMb2dpc3RpYyBSZWdyZXNzaW9uCgoKIyMjIyA4LjEgTW9yZSBSZXZpZXcKCkxvZ2FyaXRobSAobG9nKTogbG9nMl44PSBXaGljaCBwb3dlciBvZiAyIGdpdmVzIHVzIDg/IDMKCkV1bGVyJ3MgbnVtYmVyIChlKTogYW4gaXJyYXRpb25hbCBudW1iZXIgbGlrZSBwaS4gMi43MTgyODE4Cgpsb2dlXnggPSBsblggPSBOYXR1cmFsIGxvZyBmdW5jdGlvbiBvZiBYIAoKVGhlIEV4cG9uZW50aWFsIGZ1bmN0aW9uIGlzIHRoZSBpbnZlcnNlIG9mIG5hdHVyYWwgbG9nIAoKClByb2JhYmlsaXR5IFZzLiBPZGRzCgpQcm9iYWJpbGl0eTogCiAgKyBQKEEpID0gbihBKSAvIG4oUykKICArIFdoYXQgcHJvcG9ydGlvbiBvZiB0aGUgdGltZSBhbiBvdXRjb21lIGlzIHRvIG9jY3VyCiAgKyBUaGUgcHJvYmFiaWxpdHkgb2Ygcm9sbGluZyBhIDIgb24gYSA2IHNpZGVkIGRpZSA9IDEvNiAoMC4xNjcpCiAgKyBNYXhpbXVtIHZhbHVlID0gMTAwJQogICsgTWluaW11bSB2YWx1ZSA9IDAlCgpPZGRzOiBQIC8gKDEgLSBQKQogICsgUHJvYmFiaWxpdHkgaXQgd2lsbCBoYXBwZW4gZGl2aWRlZCBieSB0aGUgcHJvYmFiaWxpdHkgaXQgd2lsbCBub3QgaGFwcGVuICAKICArIFRoZSBvZGRzIHRoYXQgeW91IHdpbGwgc2NvcmUgYSAyID0gMC4xNjcgLyAoMSAtIDAuMTY3KSA9IDAuMiBbMS82IC8gKDEgLSAxLzYpID0gMS81XQogICsgTWF4aW11bSB2YWx1ZSA9IEluZmluaXR5CiAgKyBNaW5pbXVtIHZhbHVlID0gMCUKICAKCiMjIyMgOC4yIEludHJvZHVjdGlvbgpgYGB7cn0KIyBMb2FkIExpYnJhcnkKbGlicmFyeShjYXJldCkKCiMgTG9hZCB0aGUgZGF0YQpsb2FkKCIvVXNlcnMvdGltaHVsYWsvRGVza3RvcC9TeXJhY3VzZS9GSU4tNjU0XCBGaW5hbmNpYWxcIEFuYWx5dGljcy9XZWVrXCA4LzhfdGl0YW5pYy5yZGF0YSIpCgpoZWFkKFRpdGFuaWMpCmBgYAoKVGFzazogQnVpbGQgQmF5ZXNpYW4gQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24gdGhhdCBwcmVkaWN0cyB0aGUgcHJvYmFiaWxpdHkgb2Ygc3Vydml2aW5nIApgYGB7cn0KSUNFQkVSRyA8LSB0cmFpbihTVVJWSVZFRCB+IC4sIGRhdGEgPSBUaXRhbmljLCBtZXRob2QgPSAiYmF5ZXNnbG0iKQoKc3VtbWFyeShJQ0VCRVJHKQpgYGAKCkNvbnNpZGVyIGFuIDE4IHllYXIgb2xkIG1hbGUgcGFzc2VuZ2VyIGluIDNyZCBjbGFzcwpgYGB7cn0KSkFDSyA8LSBkYXRhLmZyYW1lKENMQVNTPSd0aGlyZCcsIEdFTkRFUj0nbWFsZScsIEFHRT0xOCkKCnByZWRpY3QoSUNFQkVSRywgSkFDSywgdHlwZSA9ICJwcm9iIikKCmBgYAoKQ29uc2lkZXIgYW4gMTggeWVhciBvbGQgZmVtYWxlIHBhc3NlbmdlciBpbiAxc3QgY2xhc3MKYGBge3J9ClJPU0UgPC0gZGF0YS5mcmFtZShDTEFTUz0nZmlyc3QnLCBHRU5ERVI9J2ZlbWFsZScsIEFHRT0xOCkKCnByZWRpY3QoSUNFQkVSRywgUk9TRSwgdHlwZSA9ICJwcm9iIikKYGBgCgoKIyMjIyA4LjMgQ3JlZGl0IFJpc2sgUHJlZGljdGlvbgoKKktOTjogcmVxdWlyZXMgCiAgKyBSZXF1aXJlcyBwcmUgcHJvY2Vzc2luZyBvZiBwcmVkaWN0b3IgdmFyaWFibGVzIAogICsgTmVlZCB0byBzZXQuc2VlZCgpIGZvciByZXByb2R1Y2libGUgcmVzdWx0cwogICsgQ2FuIGFwcGx5IHRvIHByb2JsZW1zIHdpdGggbW9yZSB0aGF0IHR3byBjbGFzc2VzCiogQmF5ZXNpYW4gQmluYXJ5IExvZ2lzdGljIFJlZ3Jlc3Npb24KICArIERvIG5vdCBuZWVkIHRvIHByZXByb2Nlc3MgcHJlZGljdG9yIHZhcmlhYmxlcyAKICArIERvIG5vdCBuZWVkIHRvIHNldC5zZWVkKCkKICArIElzIG5vdCBhcHBsaWNhYmxlIHRvIHByb2JsZW1zIHdpdGggbW9yZSB3aXRoIG1vcmUgdGhhbiB0d28gY2xhc3NlcyAKCmBgYHtyfQojIExvYWQgTGlicmFyeQpsaWJyYXJ5KGNhcmV0KQoKbG9hZCgiL1VzZXJzL3RpbWh1bGFrL0Rlc2t0b3AvU3lyYWN1c2UvRklOLTY1NFwgRmluYW5jaWFsXCBBbmFseXRpY3MvV2Vla1wgOC84X2hvbWVfbG9hbi5yZGF0YSIpCgpoZWFkKEhPTUUxKQoKaGVhZChIT01FMikKYGBgCgpgYGB7cn0KIyBTZXQgdGhlIHNlZWQgZm9yIHJlcHJvZHVjaWJsZSByZXN1bHRzLiBUaGlzIHdpbGwgYWxsb3cgdGhlIGJvb3RzdHJhcHBpbmcgb2YgdGhlIEtOTiBhbGdvcml0aG0gdG8gdXNlIHRoZSBzYW1lIHJhbmRvbSBudW1iZXJzIGVhY2ggdGltZSAKc2V0LnNlZWQoMTMpCkhHVFYgPC0gdHJhaW4oREVGQVVMVCB+IC4sIGRhdGEgPSBIT01FMSwgbWV0aG9kID0gImtubiIsIHByZVByb2Nlc3MgPSBjKCJjZW50ZXIiLCAic2NhbGUiKSwgdHVuZUxlbmd0aCA9IDIwKQoKSEdUVgpgYGAKCmBgYHtyfQojIERlZmluZSAKRElZIDwtIHRyYWluKERFRkFVTFQgfi4sZGF0YT1IT01FMSxtZXRob2Q9ImJheWVzZ2xtIikKCiMgRGVmaW5lIGEgcG90ZW50aWFsIGN1c3RvbWVyCkpPSE5ET0UgPC0gZGF0YS5mcmFtZShUWVBFPSJjYXNoIiwgR0VOREVSPSJtYWxlIixDQVI9InllcyIsQ0hJTERSRU49MixSQVRJTz0yLjQwKQoKIyBXaWxsIGhlIGRlZmF1bHQgb24gaGlzIGhvbWUgbG9hbj8KcHJlZGljdChESVksSk9ITkRPRSwgdHlwZSA9ICJwcm9iIikKIyAxNyAwZiB0aGUgMzMgKDUxLjUyJSkgbmVhcmVzdCBuZWlnaGJvcnMgZGVmYXVsdGVkIHdoZXJlIGFzIDE2ICg0OC40OCUpIGRpZCBub3QKcHJlZGljdChIR1RWLEpPSE5ET0UsIHR5cGUgPSAicHJvYiIpCmBgYAoKVGVzdCB0aGUgbW9kZWxzIAoKYGBge3J9CnRhaWwoSE9NRTIpCmBgYAoKYGBge3J9CiMgVGhlIEtOTiBtb2RlbCBpcyA1NS43NSUgYWNjdXJhdGUKIyBBY2N1cmFjeSA6IDAuNTYKaGd0diA8LSBwcmVkaWN0KEhHVFYsIEhPTUUyKQojIEJ1aWxkIGNvbmZ1c2lvbiBtYXRyaXggdG8gY2hlY2sKY29uZnVzaW9uTWF0cml4KGhndHYsIEhPTUUyJERFRkFVTFQpCmBgYAoKCmBgYHtyfQojIExvZ2lzdGljIFJlZ3Jlc3Npb24gaXMgNjAuMjUlIGFjY3VyYXRlCiMgQWNjdXJhY3kgOiAwLjYwMjUgCmRpeSA8LSBwcmVkaWN0KERJWSwgSE9NRTIpCmNvbmZ1c2lvbk1hdHJpeChkaXksIEhPTUUyJERFRkFVTFQpCmBgYAoKVGhlICJObyBJbmZvcm1hdGlvbiBSYXRlIiBpcyB0aGUgbW9zdCBjb21tb24gY2xhc3MgKGluIHRoaXMgY2FzZSwgNjUuNzUlIGFyZSBkZWZhdWx0ID0geWVzKQoKVGhlcmVmb3JlLCBzaW5jZSBib3RoIG1vZGVscyBkaWQgbm90IHBlcmZvcm0gYmV0dGVyIHRoYW4gdGhlICJObyBJbmZvcm1hdGlvbiBSYXRlIiwgbmVpdGhlciBtb2RlbCBpcyBhIGdvb2Qgb25lLiBJbiBvcmRlciB0byByYWlzZSB0aGUgYWNjdXJhY3ksIHlvdSB3aWxsIG5lZWQgdG8gY29sbGVjdCBtb3JlIGRhdGEgb3IgYWRkIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlcy4gCgojIFdlZWsgOTogTWFjaGluZSBMZWFybmluZzogSW50cm9kdWN0aW9uIHRvIEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmtzCgoKCiMjIyMgOS4xIEludHJvZHVjdGlvbgoKUXVhbnRpdGF0aXZlIHByZWRpY3Rpb25zIHN1Y2ggYXMgImhvdyBtdWNoIHJldmVudWUgd2lsbCBhIG1vdmllIGdlbmVyYXRlIiBvciAid2hhdCB3b3VsZCBiZSBhIGZhaXIgc2VsbGluZyBwcmljZSBmb3IgYSBwcm9wZXJ0eSIgCgojIyMjIDkuMiBBcnRpZmljaWFsIE5ldXJhbCBOZXR3b3JrcyB3aXRoIEh5cGVyYm9saWMgVGFuZ2VudCBBY3RpdmF0aW9uIEZ1bmN0aW9uCgpUaGUgSHlwZXJib2xpYyBUYW5nZW50IHJhbmdlcyBmcm9tIC0xIHRvIDEuIAoKSWYgWCA+PSAzLCB0aGVuIFRhbkggaXMgYXBwcm94aW1hdGVseSBlcXVhbCB0byAxLiBJZiBYIDw9IC0zLCB0aGVuIFRhbkggaXMgYXBwcm94aW1hdGVseSBlcXVhbCB0byAtMQoKCgpgYGB7cn0KIyBMb2FkIHRoZSBEYXRhCmxvYWQoIi9Vc2Vycy90aW1odWxhay9EZXNrdG9wL1N5cmFjdXNlL0ZJTi02NTRcIEZpbmFuY2lhbFwgQW5hbHl0aWNzL1dlZWtcIDkvOV9hbWVzLnJkYXRhIikKCnRhaWwoYW1lcykKYGBgClByZWRpY3QgdGhlIGZhaXIgc2VsbGluZyBwcmljZSBvZiBhIHByb3BlcnR5LgoKKlRoZSBkYXRhIGluY2x1ZGU6CiAgKyBQcmljZTogU2VsbGluZyBwcmljZSBkaXZpZGVkIGJ5ICQxLDAwMCwwMDAKICArIFJvb21zOiBOdW1iZXIgb2Ygcm9vbXMgZGl2aWRlZCBieSAxMAogICsgQmF0aHM6IE51bWJlciBvZiBiYXRocm9vbXMgZGl2aWRlZCBieSAxMAogICsgS2l0Y2hlbnM6IE51bWJlciBvZiBraXRjaGVucyBkaXZpZGVkIGJ5IDEwCiAgKyBDYXJzOiAgY2FyIGNhcGFjaXR5IG9mIGdhcmFnZSBkaXZpZGVkIGJ5IDEwCgpXaGVuIGJ1aWxkaW5nIGFydGlmaWNpYWwgbmV1cmFsIG5ldHdvcmtzLCB0aGUgZGF0YSBzaG91bGQgaGF2ZSAic21hbGwiIHZhbHVlcyAoZS5nLiBiZXR3ZWVuIC0zIGFuZCAzKSB0byBzYXRpc2Z5IHRoZSBIeXBlcmJvbGljIFRhbmdlbnQuIFRoZSAic21hbGxlciIsIHRoZSBiZXR0ZXIuIAoKYGBge3J9CiNpbnN0YWxsLnBhY2thZ2VzKCJuZXVyYWxuZXQiKQpsaWJyYXJ5KG5ldXJhbG5ldCkKCkxPR0NBQklOIDwtIG5ldXJhbG5ldChQUklDRSB+IC4sIGRhdGEgPSBhbWVzLCBoaWRkZW4gPSAxLCBhY3QuZmN0ID0gInRhbmgiKQoKcGxvdChMT0dDQUJJTikKYGBgCgpFYWNoIHRpbWUgeW91IGJ1aWxkIHRoZSBtb2RlbCwgeW91IHdpbGwgZ2V0IGRpZmZlcmVudCB3ZWlnaHRzLiBUaGUgYWxnb3JpdGhtIHN0YXJ0cyB3aXRoIHJhbmRvbSBjb2VmZmljaWVudHMgYW5kIGNvbnZlcmdlcyB0byBhIG5lYXJieSBsb2NhbCBvcHRpbXVtIHNvbHV0aW9uLiBUaGUgbG9jYWwgb3B0aW11bSBzb2x1dGlvbiBtYXkgZGlmZmVyIGJlY2F1c2UgaXQgdXNlcyBkaWZmZXJlbnQgcmFuZG9tIG51bWJlcnMuIFlvdSBjYW4gc2V0LnNlZWQoKSB0byByZXByb2R1Y2UgcmVzdWx0cyAodGhlIHNlZWQgbnVtYmVyIGRvZXMgbm90IG1hdHRlcikgYW5kIHRlbGwgdGhlIGFsZ29yaXRobSB0byB1c2UgdGhlIHNhbWUgcmFuZG9tIG51bWJlcnMgZWFjaCB0aW1lIGl0IGlzIHJ1bi4gCgpgYGB7cn0Kc2V0LnNlZWQoMSkKTE9HQ0FCSU4gPC0gbmV1cmFsbmV0KFBSSUNFIH4gLiwgZGF0YSA9IGFtZXMsIGhpZGRlbiA9IDEsIGFjdC5mY3QgPSAidGFuaCIpCgpwbG90KExPR0NBQklOKQpgYGAKCkFuYXRvbXk6IElOUFVUIG5vZGVzIChwcmVkaWN0b3JzKSwgSElEREVOIG5vdGUocyksIE9VVFBVVCBub3RlIChwcmVkaWN0ZWQpLiBUaGVyZSBhcmUgYWxzbyAiQklBUyIgdGVybXMgKHRoZSBibHVlIGxpbmVzKS4gVGhlIG51bWJlcnMgYXJlIHRoZSAid2VpZ2h0cyIuIAoKCgojIFN0ZXAtYnktc3RlcDoKVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBwcmljZSBvZiBhIGhvdXNlIHRoYXQgaGFzIDcgcm9vbXMsIDMgYmF0aHJvb21zLCAxIGtpdGNoZW4sIGFuZCBhIGdhcmFnZSBmb3IgMiBjYXJzLgoKSW4gdGhlIGNhc2Ugb2YgdGhlIGFib3ZlLCB0aGUgdmFsdWUgb2YgZWFjaCBwcmVkaWN0b3IgaXMgbXVsdGlwbGllZCBieSB0aGUgd2VpZ2h0LiBUaGUgQklBUyB0ZXJtcyBhcmUgbXVsdGlwbGllZCBieSB0aGUgbnVtYmVyIGluIHRoZSBub2RlIGJ5IGl0J3Mgd2VpZ2h0IChpbiB0aGlzIGNhc2UsIDEgKiAwLjA0NzE5KS4gU2VlIEJlbG93OgoKICArIEJJQVM6IDEgKiAwLjA0NzE5CiAgKyBST09NUzogMC43ICogMC4wNjI2MwogICsgQkFUSFM6IDAuMyAqIDAuMTYzNjgKICArIEtJVENIRU5TOiAwLjEgKiAtMC4yODcyOQogICsgQ0FSUzogMC4yICogMC4yNDM3MgoKVGhlIHN1bSBvZiB0aGUgYWJvdmUsIDAuMTYwMTUsIGlzIHRoZSB2YWx1ZSB0aGF0IGZsb3dzIGludG8gdGhlIEhJRERFTiBOT0RFLiBUaGUgaGlkZGVuIG5vZGUgaW5jbHVkZXMgdGhlIGFjdGl2YXRpb24gZnVuY3Rpb24gKGluIHRoaXMgY2FzZSwgdGhlIEh5cGVyYm9saWMgVGFuZ2VudCkuIAoKYGBge3J9CjEgKiAwLjA0NzE5CjAuNyAqIDAuMDYyNjMKMC4zICogMC4xNjM2OAowLjEgKiAtMC4yODcyOQowLjIgKiAwLjI0MzcyCmBgYAoKVGhlIGhpZGRlbiBub2RlIGlzIGdvaW5nIHRvIGFwcGx5IHRoZSBIeXBlcmJvbGljIFRhbmdlbnQgZnVuY3Rpb24gdG8gdGhlIHZhbHVlIG9mIDAuMTYwMTUuCmBgYHtyfQpGTE9XIDwtICgxICogMC4wNDcxOSkgKyAoMC43ICogMC4wNjI2MykgKyAoMC4zICogMC4xNjM2OCkgKyAoMC4xICogLTAuMjg3MjkpKyAoMC4yICogMC4yNDM3MikKRkxPVwp0YW5oKEZMT1cpCmBgYAoKVGhlIG91dGZsb3cgZnJvbSB0aGUgSElEREVOIE5PREUgaGFzIGl0J3Mgb3duIHdlaWdodCwgdGhlIHRoZSByZXN1bHQgb2YgdGhlIEh5cGVyYm9saWMgVGFuZ2VudCBmdW5jdGlvbiBpcyBtdWx0aXBsaWVkIGJ5IHRoZSB3ZWlnaHQuIFRoZW4gdGhlIHJlc3VsdCBpcyBtdWx0aXBsaWVkIGJ5IHRoZSAybmQgQklBUyBURVJNLCAxICogLTAuMDU4ODYuIFRoaXMgaXMgYmVjYXVzZSBib3RoIG9mIHRob3NlIHRlcm1zIGZsb3cgaW50byB0aGUgT1VUUFVUIE5PREUuIEJ5IGRlZmF1bHQsIHRoZXJlIGlzIG5vIGFjdGl2YXRpb24gZnVuY3Rpb24gaW4gdGhlIE9VVFBVVCBOT0RFLgpgYGB7cn0KKDAuMTU4Nzk0NyAqIDEuNjk4NjQpICsgKDEgKiAtMC4wNTg4NikKYGBgCgpGaW5hbGx5LCBjb252ZXJ0IHRoZSByZXN1bHQgdG8gZG9sbGFycyBieSBtdWx0aXBseWluZyBpdCBieSAxLDAwMCwwMDAgKHdoaWNoIGlzIHdoYXQgd2UgZGl2aWRlZCBieSBpbiBvcmRlciB0byBtYWtlIHRoZSBpbnB1dCB2YWx1ZXMgInNtYWxsIikKCmBgYHtyfQpPVVRQVVQgPC0gKDAuMTU4Nzk0NyAqIDEuNjk4NjQpICsgKDEgKiAtMC4wNTg4NikKSE9NRV9QUklDRSA8LSBPVVRQVVQgICogMTAwMDAwMApIT01FX1BSSUNFCmBgYAoKVXNpbmcgdGhlIEFsZ29yaXRobQoKVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBwcmljZSBvZiBhIGhvdXNlIHRoYXQgaGFzIDcgcm9vbXMsIDMgYmF0aHJvb21zLCAxIGtpdGNoZW4sIGFuZCBhIGdhcmFnZSBmb3IgMiBjYXJzLgpgYGB7cn0KTkVTVCA8LSBkYXRhLmZyYW1lKFJPT01TPTcvMTAsIEJBVEhTPTMvMTAsIEtJVENIRU5TPTEvMTAsIENBUlM9Mi8xMCkKCk5FU1QKYGBgCgpgYGB7cn0KcHJlZGljdChMT0dDQUJJTiwgTkVTVCkKYGBgCgpgYGB7cn0KcHJlZGljdChMT0dDQUJJTiwgTkVTVCkgKiAxMDAwMDAwCmBgYAoKVXNlIHRoZSBtb2RlbCBhbmQgcHJlZGljdCB0aGUgImZhaXIiIHNlbGxpbmcgcHJpY2Ugb2YgYSBob3VzZSBpbiBBbWVzLCBJb3dhLCB0aGF0IGhhcyBuaW5lIHJvb21zLCBmb3VyIGJhdGhyb29tcywgb25lIGtpdGNoZW4sIGFuZCBhIGdhcmFnZSBmb3IgdHdvIGNhcnMuCgoibWFudWFsIgpgYGB7cn0KcGxvdChMT0dDQUJJTikKYGBgCgpgYGB7cn0KIyBDYWxjdWxhdGUgVmFsdWVzOiBYICogd2VpZ2h0CkJJQVMxIDwtIDEgKiAwLjA0NzE5CnJvb21zIDwtICg5IC8gMTApICogMC4wNjI2MwpiYXRocyA8LSAoNCAvIDEwKSAqIDAuMTYzNjgKa2l0Y2hlbnMgPC0gKDEgLyAxMCkgKiAtMC4yODcyOQpjYXJzIDwtICgyIC8gMTApICogMC4yNDM3MgpCSUFTMiA8LSAxICogLTAuMDU4ODYKCiMgQ2FsY3VsYXRlIHRoZSBmbG93IGludG8gdGhlIGhpZGRlbiBub2RlCkhJRERFTiA9IEJJQVMxICsgcm9vbXMgKyBiYXRocyArIGtpdGNoZW5zICsgY2FycwoKIyBDYWxjdWxhdGUgdGhlIG91dHB1dApvdXRwdXQgPC0gKHRhbmgoSElEREVOKSAqIDEuNjk4NjQpICsgQklBUzIKCiMgbXVsdGlwbHkgdGhlIG91dHB1dCB0byBnZXQgcHJlZGljdGVkIHByaWNlCm91dHB1dCAqIDEwMDAwMDAKYGBgCgoiYXV0b21hdGljIgpgYGB7cn0KTkVXX0hPTUUgPC0gZGF0YS5mcmFtZShST09NUz05LzEwLCBCQVRIUz00LzEwLCBLSVRDSEVOUz0xLzEwLCBDQVJTPTMvMTApCgpwcmVkaWN0KExPR0NBQklOLCBORVdfSE9NRSkgKiAxMDAwMDAwCmBgYAoKCiMjIyMgOS4zIEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmtzIHdpdGggTG9naXN0aWMgKFNpZ21vaWQpIEFjdGl2YXRpb24gRnVuY3Rpb24KYGBge3J9CiMgTG9naXN0aWMgZnVuY3Rpb24gCnBsb2dpcygyKQpgYGAKCldoZW4gdGhlIGxvZ2lzdGljIGZ1bmN0aW9ucyBpcyA+PSA2LCBpdCBpcyBhc3ltcHRvdGljIGFuZCB2aXJ0dWFsbHkgZXF1YWwgdG8gMS4gV2hlbiB0aGUgbG9naXN0aWMgZnVuY3Rpb25zIGlzIDw9IC02LCBpdCBpcyBhc3ltcHRvdGljIGFuZCB2aXJ0dWFsbHkgZXF1YWwgdG8gMC4gCgpgYGB7cn0KbGlicmFyeShuZXVyYWxuZXQpCgpsb2FkKCIvVXNlcnMvdGltaHVsYWsvRGVza3RvcC9TeXJhY3VzZS9GSU4tNjU0XCBGaW5hbmNpYWxcIEFuYWx5dGljcy9XZWVrXCA5LzlfaG9sbHl3b29kLnJkYXRhIikKCnRhaWwoSE9MTFlXT09EKQpgYGAKClN1cHBvc2UgeW91IGFyZSBhIG1vdmllIHByb2R1Y2VyIGFuZCB5b3Ugd2FudCB0byBwcmVkaWN0IGhvdyBtdWNoIHJldmVudWUgYSBtb3ZpZSB3aWxsIGdlbmVyYXRlLgpGb3IgdGhpcyBwdXJwb3NlLCB5b3UgY29sbGVjdCBkYXRhIGZyb20gYSByYW5kb20gc2FtcGxlIG9mIDEsMzAwIG1vdmllcyBmcm9tIHRoZSBtb3ZpZSBkYXRhYmFzZSAod3d3LnRoZW1vdmllZGIub3JnKS4gRm9yIGVhY2ggbW92aWUsIHRoZSBkYXRhIHNldCBpbmNsdWRlcyB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoKCiAgKyBSRVZFTlVFIDogVGhlIHJldmVudWUgZ2VuZXJhdGVkIGJ5IHRoZSBtb3ZpZSBpbiAkQmlsbGlvbgogICsgQlVER0VUIDogVGhlIGJ1ZGdldCByZXF1aXJlZCBmb3IgcHJvZHVjaW5nIHRoZSBtb3ZpZSBpbiAkQmlsbGlvbi4KICArIFJVTlRJTUUgOiBUaGUgcnVudGltZSBvZiB0aGUgbW92aWUgaW4gaG91cnMuCiAgKyBIT1JST1IgOiBJZiB0aGUgbW92aWUgZ2VucmUgaXMgaG9ycm9yIG9yIHRocmlsbGVyLCBpdCBlcXVhbHMgMS4gT3RoZXJ3aXNlLCBpdCBpcyAwLgogICsgUi5SQVRFRCA6IElmIHRoZSBtb3ZpZSBpcyBSIHJhdGVkLCBpdCBlcXVhbHMgMS4gT3RoZXJ3aXNlLCBpdCBpcyAwLgoKVXNlIFIgJiBidWlsZCBhbiBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrIHRoYXQgcHJlZGljdHMgdGhlIFJFVkVOVUUgZ2VuZXJhdGVkIGZyb20gYSBtb3ZpZS4gSW5jbHVkZSBvbmUgaGlkZGVuIG5vZGUgaW4gdGhlIGFydGlmaWNpYWwgbmV1cmFsIG5ldHdvcmsuClVzZSBsb2dpc3RpYyAoc2lnbW9pZCkgYXMgdGhlIGFjdGl2YXRpb24gZnVuY3Rpb24uCgpgYGB7cn0KIyBTZXQgdGhlIHNlZWQKc2V0LnNlZWQoMSkKCiMgQ3JlYXRlIHRoZSBtb2RlbCB3aXRoIHRoZSBsb2dpc3RpYyBmdW5jdGlvbgpQT1BDT1JOIDwtIG5ldXJhbG5ldChSRVZFTlVFIH4gLiwgZGF0YSA9IEhPTExZV09PRCwgaGlkZGVuID0gMSwgYWN0LmZjdCA9ICJsb2dpc3RpYyIpCgojIFBsb3QgdGhlIG1vZGVsCnBsb3QoUE9QQ09STikKYGBgCmBgYHtyfQp0YWlsKEhPTExZV09PRCkKYGBgCgoKYGBge3J9ClRIRV9HUkVZID0gZGF0YS5mcmFtZShCVURHRVQ9MjUwMDAwMDAvMTAwMDAwMDAwMCwgUlVOVElNRT0xMTcvNjAsIEhPUlJPUj0wLCBSLlJBVEVEPTEpCgpwcmVkaWN0KFBPUENPUk4sIFRIRV9HUkVZKSAqIDEwMDAwMDAwMDAKYGBgCgpgYGB7cn0KSEFMTE9XRUVOX0VORFMgPC0gZGF0YS5mcmFtZShCVURHRVQ9MTUwMDAwMDAvMTAwMDAwMDAwMCwgUlVOVElNRT0xMDUvNjAsIEhPUlJPUj0xLCBSLlJBVEVEPTEpCgpwcmVkaWN0KFBPUENPUk4sIEhBTExPV0VFTl9FTkRTKSAqIDEwMDAwMDAwMDAKYGBgCgoKIyAxMDogTWFjaGluZSBMZWFybmluZzogVHJhaW5pbmcgYW5kIFRlc3RpbmcgQXJ0aWZpY2lhbCBOZXVyYWwgTmV0d29ya3MKCiMjIyMgMTAuMSBNb3JlIFJldmlldwoKRGVwZW5kZW50IFZhcmlhYmxlIG9uIHRoZSBZIGF4aXMgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlIG9uIHRoZSBYIGF4aXMgKEluY29tZSBkZXBlbmRzIG9uIGFnZSkKCjIwMTYgQW5udWFsIGluY29tZSBmb3IgY2VsZWJyaXRpZXMgb24gbWlsbGlvbnMgb2YgZG9sbGFycyAKYGBge3J9CmxpYnJhcnkoc2NhdHRlckQzKQoKbG9hZCgiL1VzZXJzL3RpbWh1bGFrL0Rlc2t0b3AvU3lyYWN1c2UvRklOLTY1NFwgRmluYW5jaWFsXCBBbmFseXRpY3MvV2Vla1wgMTAvY29ycmVsYXRpb24ucmRhdGEiKQoKaGVhZChDRUxFQikKYGBgCgpgYGB7cn0KcGxvdCh4ID0gQ0VMRUIkQUdFLCB5ID0gQ0VMRUIkVVNELCB4bGFiID0gIkFnZSAoMjAxNikiLCB5bGFiID0gIkluY29tZSAoJE1pbGxpb24pIikKYGBgClRoZXJlIGlzIGEgcGF0dGVybiwgYXMgQUdFIGluY3JlYXNlcyBJTkNPTUUgdGVuZHMgdG8gZ28gZG93bi4gCgpgYGB7cn0Kc2NhdHRlckQzKHggPSBDRUxFQiRBR0UsIHkgPSBDRUxFQiRVU0QsIHhsYWIgPSAiQWdlICgyMDE2KSIsIHlsYWIgPSAiSW5jb21lICgkTWlsbGlvbikiLCBjb2xvciA9ICJncmVlbiIsIGhvdmVyX3NpemUgPSA0KQpgYGAKCkNvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IApgYGB7cn0KIyBBZ2UgYW5kIFVTRApjb3IoQ0VMRUIkQUdFLCBDRUxFQiRVUykKCiMgQWdlIGFuZCBVU0QKY29yKENFTEVCJEFHRSwgQ0VMRUIkRVVSTykKYGBgCgpUaGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgbmVnYXRpdmUsIHdoaWNoIGRlbW9uc3RyYXRlcyBhIG5lZ2F0aXZlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBBR0UgQW5kIElOQ09NRS4oUGVhcnNvbidzIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IC0xIDw9IFggPD0gMSkKCgoKIyMjIyAxMC4yIFRyYWluaW5nIGFuZCBUZXN0aW5nIEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmsgTW9kZWxzCgoKU3VwcG9zZSB5b3UgYXJlIGEgcmVhbCBlc3RhdGUgYWdlbnQgYXQgTmFzaHZpbGxlLCBUTi4gWW91IHdhbnQgdG8gcHJlZGljdCB0aGUgImZhaXIiIHNlbGxpbmcgcHJpY2Ugb2YgYSBwcm9wZXJ0eS4gRm9yIHRoaXMgcHVycG9zZSwgeW91IGNvbGxlY3QgZGF0YSBmcm9tIGEgcmFuZG9tIHNhbXBsZSBvZiAxLDIwMCBwcm9wZXJ0aWVzIGF0IE5hc2h2aWxsZSwgVE4uCgpGb3IgZWFjaCBwcm9wZXJ0eSwgdGhlIGRhdGEgc2V0IGluY2x1ZGVzIHRoZSBmb2xsb3dpbmcgaW5mb3JtYXRpb246CgogICsgUFJJQ0UgOiBUaGUgc2VsbGluZyBwcmljZSBvZiB0aGUgcHJvcGVydHkgZGl2aWRlZCBieSAkMSwwMDAsMDAwIAogICsgQUNSRVMgOiBUaGUgbG90IHNpemUgaW4gYWNyZXMgZGl2aWRlZCBieSAxMAogICsgQVJFQSA6IFRoZSBsaXZpbmcgYXJlYSBpbiBzcXVhcmUtZmVldCBkaXZpZGVkIGJ5IDEsMDAwCiAgKyBZRUFSIDogVGhlIHllYXIgd2hlbiB0aGUgcHJvcGVydHkgd2FzIGJ1aWx0IGRpdmlkZWQgYnkgMSwwMDAgCiAgKyBCQVRIUyA6IFRoZSBudW1iZXIgb2YgYmF0aHJvb21zIGRpdmlkZWQgYnkgMTAKICArIEJSSUNLIDogSWYgdGhlIGhvdXNlIGhhcyBicmljayBleHRlcmlvciB3YWxsLCBpdCBlcXVhbHMgMTsgb3RoZXJ3aXNlLCAwLiAKICArIEJBU0VNRU5UIDogSWYgdGhlIGhvdXNlIGhhcyBiYXNlbWVudCwgaXQgZXF1YWxzIDE7IG90aGVyd2lzZSwgMC4KICAKICAKVXNlIFIgJiBidWlsZCBhbiBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrIHRoYXQgcHJlZGljdHMgdGhlIFBSSUNFIG9mIGEgcHJvcGVydHkuIEluY2x1ZGUgZm91ciBoaWRkZW4gbm9kZXMgaW4gdGhlIGFydGlmaWNpYWwgbmV1cmFsIG5ldHdvcmsuClVzZSBsb2dpc3RpYyAoc2lnbW9pZCkgYXMgdGhlIGFjdGl2YXRpb24gZnVuY3Rpb24uCgpgYGB7cn0KbG9hZCgiL1VzZXJzL3RpbWh1bGFrL0Rlc2t0b3AvU3lyYWN1c2UvRklOLTY1NFwgRmluYW5jaWFsXCBBbmFseXRpY3MvV2Vla1wgMTAvMTBfbmFzaHZpbGxlLnJkYXRhIikKCmhlYWQoTkFTSDEpCgpoZWFkKE5BU0gyKQpgYGAKClVzZSBSICYgYnVpbGQgYW4gYXJ0aWZpY2lhbCBuZXVyYWwgbmV0d29yayB0aGF0IHByZWRpY3RzIHRoZSBQUklDRSBvZiBhIHByb3BlcnR5LiBJbmNsdWRlIGZvdXIgaGlkZGVuIG5vZGVzIGluIHRoZSBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrLgpVc2UgbG9naXN0aWMgKHNpZ21vaWQpIGFzIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uLgoKYGBge3J9CmxpYnJhcnkobmV1cmFsbmV0KQoKc2V0LnNlZWQoOTkpCk1BTkNBVkUgPC0gbmV1cmFsbmV0KFBSSUNFIH4gLiwgZGF0YSA9IE5BU0gxLCBoaWRkZW4gPSA0LCBhY3QuZmN0ID0gImxvZ2lzdGljIikKCnBsb3QoTUFOQ0FWRSkKYGBgCk11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIAoKYGBge3J9ClNIRVNIRUQgPC0gbG0oUFJJQ0UgfiAuLCBkYXRhID0gTkFTSDEpClNIRVNIRUQKYGBgCgpgYGB7cn0KdGFpbChOQVNIMikKYGBgCgpgYGB7cn0KbWFuY2F2ZSA8LSBwcmVkaWN0KE1BTkNBVkUsIE5BU0gyKQp0YWlsKG1hbmNhdmUpCmBgYAoKVGVzdCB0aGUgQXJ0aWZpY2lhbCBOZXVyYWwgTmV0d29yayBtb2RlbHMgcGVyZm9ybWFuY2Ugd2l0aCB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQKYGBge3J9CmNvcihtYW5jYXZlLCBOQVNIMiRQUklDRSkKYGBgCgpgYGB7cn0Kc2hlc2hlZCA8LSBwcmVkaWN0KFNIRVNIRUQsIE5BU0gyKQp0YWlsKHNoZXNoZWQpCmBgYAoKVGVzdCB0aGUgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzIHBlcmZvcm1hbmNlIHdpdGggdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50CmBgYHtyfQpjb3Ioc2hlc2hlZCwgTkFTSDIkUFJJQ0UpCmBgYAoKU2luY2UgdGhlIEFydGlmaWNpYWwgTmV1cmFsIE5ldHdvcmsgbW9kZWwgcGVyZm9ybWVkIGJldHRlciwgbGV0cyB1c2UgaXQgdG8gbWFrZSBhIHByZWRpY3Rpb24KYGBge3J9CiMgVGhlIGZvbGxvd2luZyBob3VzZSBzb2xkIGZvciAkMTY1LDAwMC4gTGV0J3Mgc2VlIGhvdyBjbG9zZSB0aGUgbW9kZWxzIGdldCAKCkZPUlNBTEUgPC0gZGF0YS5mcmFtZShBQ1JFUyA9IDAuMDE5LCBBUkVBID0gMS41NjYsIFlFQVIgPSAxLjk4NCwgQkFUSFMgPSAwLjMsIEJSSUNLID0gMSwgQkFTRU1FTlQgPSAwKQoKRk9SU0FMRQpgYGAKCmBgYHtyfQpwcmVkaWN0KFNIRVNIRUQsIEZPUlNBTEUpICogMTAwMDAwMApgYGAKCmBgYHtyfQpwcmVkaWN0KE1BTkNBVkUsIEZPUlNBTEUpICogMTAwMDAwMApgYGAKCgojIyMjIDEwLjMgSW50cm9kdWN0aW9uIHRvIERlZXAgTGVhcm5pbmcKClN1cHBvc2UgeW91IGFyZSBhIG1vdmllIHByb2R1Y2VyIGFuZCB5b3Ugd2FudCB0byBwcmVkaWN0IGhvdyBtdWNoIHJldmVudWUgYSBtb3ZpZSB3aWxsIGdlbmVyYXRlLgpGb3IgdGhpcyBwdXJwb3NlLCB5b3UgY29sbGVjdCBkYXRhIGZyb20gYSByYW5kb20gc2FtcGxlIG9mIDEsNzY3IG1vdmllcyBmcm9tIHRoZSBtb3ZpZSBkYXRhYmFzZSAod3d3LnRoZW1vdmllZGIub3JnKS4gRm9yIGVhY2ggbW92aWUsIHRoZSBkYXRhIHNldCBpbmNsdWRlcyB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoKCiAgKyBSRVZFTlVFIDogVGhlIHJldmVudWUgZ2VuZXJhdGVkIGJ5IHRoZSBtb3ZpZSBpbiAkQmlsbGlvbgogICsgQlVER0VUIDogVGhlIGJ1ZGdldCByZXF1aXJlZCBmb3IgcHJvZHVjaW5nIHRoZSBtb3ZpZSBpbiAkQmlsbGlvbi4KICArIFJVTlRJTUUgOiBUaGUgcnVudGltZSBvZiB0aGUgbW92aWUgaW4gaG91cnMuCiAgKyBIT1JST1IgOiBJZiB0aGUgbW92aWUgZ2VucmUgaXMgaG9ycm9yIG9yIHRocmlsbGVyLCBpdCBlcXVhbHMgMS4gT3RoZXJ3aXNlLCBpdCBpcyAwLgogICsgUi5SQVRFRCA6IElmIHRoZSBtb3ZpZSBpcyBSIHJhdGVkLCBpdCBlcXVhbHMgMS4gT3RoZXJ3aXNlLCBpdCBpcyAwLgogIAoKVXNlIFIgJiBidWlsZCBhbiBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrIHRoYXQgcHJlZGljdHMgdGhlIFJFVkVOVUUgZ2VuZXJhdGVkIGZyb20gYSBtb3ZpZS4gSW5jbHVkZSB0aHJlZSBoaWRkZW4gbm9kZXMgaW4gdGhlIGFydGlmaWNpYWwgbmV1cmFsIG5ldHdvcmsuClVzZSBsb2dpc3RpYyAoc2lnbW9pZCkgYXMgdGhlIGFjdGl2YXRpb24gZnVuY3Rpb24uCmBgYHtyfQpsb2FkKCIvVXNlcnMvdGltaHVsYWsvRGVza3RvcC9TeXJhY3VzZS9GSU4tNjU0XCBGaW5hbmNpYWxcIEFuYWx5dGljcy9XZWVrXCAxMC8xMF9kZWVwLnJkYXRhIikKCmhlYWQoSE9MTFlXT09EKQoKaGVhZChXQUxLT0ZGQU1FKQpgYGAKClVzZSBSICYgYnVpbGQgYW4gYXJ0aWZpY2lhbCBuZXVyYWwgbmV0d29yayB0aGF0IHByZWRpY3RzIHRoZSBSRVZFTlVFIGdlbmVyYXRlZCBmcm9tIGEgbW92aWUuIEluY2x1ZGUgdGhyZWUgaGlkZGVuIG5vZGVzIGluIHRoZSBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrLgpVc2UgbG9naXN0aWMgKHNpZ21vaWQpIGFzIHRoZSBhY3RpdmF0aW9uIGZ1bmN0aW9uLgoKYGBge3J9CmxpYnJhcnkobmV1cmFsbmV0KQoKc2V0LnNlZWQoNSkKTE9XQlVER0VUIDwtIG5ldXJhbG5ldChSRVZFTlVFIH4gLiwgZGF0YSA9IEhPTExZV09PRCwgaGlkZGVuID0gMywgYWN0LmZjdCA9ICJsb2dpc3RpYyIpCgpwbG90KExPV0JVREdFVCkKCmBgYAoKQnVpbGQgYSBEZWVwIEFOTgoKTm93IGJ1aWxkIGFub3RoZXIgbW9kZWwgdGhhdCBoYXMgdHdvIGhpZGRlbiBsYXllcnMgd2l0aCB0aHJlZSAmIHR3byBoaWRkZW4Kbm9kZXMsIHJlc3BlY3RpdmVseS4KVXNlIGxvZ2lzdGljIChzaWdtb2lkKSBhcyB0aGUgYWN0aXZhdGlvbiBmdW5jdGlvbi4KCmBgYHtyfQpzZXQuc2VlZCgzKQpCTE9DS0JVU1RFUiA8LSBuZXVyYWxuZXQoUkVWRU5VRSB+IC4sIGRhdGEgPSBIT0xMWVdPT0QsIGhpZGRlbiA9IGMoMywyKSwgYWN0LmZjdCA9ICJsb2dpc3RpYyIpCgpwbG90KEJMT0NLQlVTVEVSKQpgYGAKSWYgdGhlIG5ldXJhbCBuZXR3b3JrIGhhcyBtdWx0aXBsZSBoaWRkZW4gbGF5ZXJzLCBpdCBoYXMgZGVlcCBsZWFybmluZyBhcmNoaXRlY3R1cmUuCgoKVGVzdCBib3RoIG1vZGVscyB1c2luZyB0aGUgZGF0YSBmcmFtZSBXQUxLT0ZGQU1FLiBVc2UgZWFjaCBtb2RlbCB0byBwcmVkaWN0IHRoZSByZXZlbnVlIGZyb20gdGhlIG1vdmllcyBpbiBXQUxLT0ZGQU1FLgogCmBgYHtyfQp0YWlsKFdBTEtPRkZBTUUpCmBgYAoKCmBgYHtyfQpsb3didWRnZXQgPC0gcHJlZGljdChMT1dCVURHRVQsIFdBTEtPRkZBTUUpCgp0YWlsKGxvd2J1ZGdldCkKYGBgCgoKYGBge3J9CmNvcihsb3didWRnZXQsIFdBTEtPRkZBTUUkUkVWRU5VRSkKYGBgCgoKYGBge3J9CmJsb2NrYnVzdGVyIDwtIHByZWRpY3QoQkxPQ0tCVVNURVIsIFdBTEtPRkZBTUUpCgp0YWlsKGJsb2NrYnVzdGVyKQpgYGAKCmBgYHtyfQpjb3IoYmxvY2tidXN0ZXIsIFdBTEtPRkZBTUUkUkVWRU5VRSkKYGBgCgpUaGUgZGVlcCBuZXVyYWwgbmV0d29yayBtb2RlbCBkaWQgbm90IHBlcmZvcm0gYmV0dGVyIHRoYW4gdGhlIHNpbmdsZSBsYXllciBuZXVyYWwgbmV0d29yayAodGhlIHNpbXBsZXIgbW9kZWwgZGlzcGxheWVkIHN1cGVyaW9yIHBlcmZvcm1hbmNlKS4gVGhpcyBtYXkgYmUgZHVlIHRvIG92ZXItZml0dGluZyB0aGUgbW9kZWwuIFRoaXMgbWVhbnMgdGhhdCB5b3UgYXJlIGdvaW5nIGJleW9uZCB0aGUgZ2VuZXJhbCBwYXR0ZXJuIGluIHRoZSBkYXRhIGFuZCBtb2RlbGluZyB0aGUgcmFuZG9tIG5vaXNlIGluIHRoZSBkYXRhc2V0LiAKCmBgYHtyfQojIFRoaXMgbW92aWUgZ2VuZXJhdGVkIGEgcmV2ZW51ZSBvZiAkNzEsMzE5LDUyNgpHUklTV09MRCA8LSBkYXRhLmZyYW1lKEJVREdFVCA9IDAuMDI3LCBSVU5USU1FID0gMS42LCBIT1JST1IgPSAwLCBSLlJBVEVEID0gMCkKR1JJU1dPTEQKYGBgCgoKYGBge3J9CnByZWRpY3QoTE9XQlVER0VULEdSSVNXT0xEKSAqIDEwMDAwMDAwMDAKYGBgCgoKYGBge3J9CnByZWRpY3QoQkxPQ0tCVVNURVIsR1JJU1dPTEQpICogMTAwMDAwMDAwMApgYGAKClRoZSBzaW1wbGVyIG1vZGVsIHByZWRpY3RlZCA3MiwzNTksODM1IHdoaWNoIGlzIGNsb3NlciB0byA3MSwzMTksNTI2CgpgYGB7cn0KIyBUaGlzIG1vdmllIGdlbmVyYXRlZCBhIHJldmVudWUgb2YgJDQxMSw1NTYsODI1IG1pbGxpb24KQkFUTUFOIDwtIGRhdGEuZnJhbWUoQlVER0VUID0gMC4wMzUsIFJVTlRJTUUgPSAyLjEsIEhPUlJPUiA9IDAsIFIuUkFURUQgPSAwKQpCQVRNQU4KYGBgCgpgYGB7cn0KcHJlZGljdChMT1dCVURHRVQsQkFUTUFOKSAqIDEwMDAwMDAwMDAKYGBgCgpgYGB7cn0KcHJlZGljdChCTE9DS0JVU1RFUixCQVRNQU4pICogMTAwMDAwMDAwMApgYGAKCk5laXRoZXIgbW9kZWwgd2FzIGV2ZW4gY2xvc2UuIAoKWW91IGNhbiBpbXByb3ZlIG1vZGVsIHBlcmZvcm1hbmNlIGJ5IGluY2x1ZGluZyBtb3JlIHByZWRpY3RvcnMsIHN1Y2ggYXMgQUNUSU9OID0gMCBvciAxLiAKCgo=